home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 4 / ETO Development Tools 4.iso / Tools - Objects / MacApp / MacApp 2.0.1 / MacApp CD Release / MacApp 2.0.1 (Hard Disk Ready) / Libraries / UMacAppUtilities.inc1.p < prev    next >
Text File  |  1990-10-25  |  75KB  |  2,872 lines

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
  3. {UMacAppUtilities.inc1.p}
  4. {Copyright © 1984-1990 Apple Computer, Inc.  All rights reserved.}
  5.  
  6. { These are utilities.    Treat them like language extensions. }
  7. {$W+}
  8. {$R-}
  9. {$Init-}
  10. {$OV-}
  11. {$IFC qNames}
  12. {$D+}
  13. {$ENDC}
  14.  
  15. {--------------------------------------------------------------------------------------------------}
  16.                                                         { The debugger uses some of this unit's
  17.                                                          types in it's interface so we must use
  18.                                                          externals. !!! Resolve this. }
  19.  
  20. TYPE
  21.     DebugForceOptions    = (forceOn, forceOff, forceUnchanged);
  22.  
  23. VAR
  24.     {$Push} {$J+}
  25.     GWORKPORT:            GrafPtr;                        { Found in UMacApp.p }
  26.     {$Pop}
  27.  
  28. FUNCTION DebugCanReadLn: Boolean;
  29.     EXTERNAL;
  30.  
  31. FUNCTION DebugCanWriteLn: Boolean;
  32.     EXTERNAL;
  33.  
  34. PROCEDURE DebugEndForce;
  35.     EXTERNAL;
  36.  
  37. PROCEDURE ProgramBreak(grievance: Str255);
  38.     EXTERNAL;
  39.  
  40. PROCEDURE DebugForceOutput(ToWindow, ToFile: DebugForceOptions);
  41.     EXTERNAL;
  42.  
  43. {--------------------------------------------------------------------------------------------------}
  44. {$S MAUtilitiesRes}
  45.  
  46. PROCEDURE BlockSet(destPtr: Ptr;
  47.                    byteCount: longint;
  48.                    setVal: UNIV SignedByte);
  49.  
  50. { ??? should be improved to do longword setting. }
  51.  
  52.     VAR
  53.         endPtr:             Ptr;
  54.  
  55.     BEGIN
  56.     destPtr := Ptr(StripLong(destPtr));
  57.     endPtr := Ptr(Ord(destPtr) + byteCount);
  58.     WHILE Ord(destPtr) < Ord(endPtr) DO
  59.         BEGIN
  60.         destPtr^ := setVal;
  61.         destPtr := Ptr(Ord(destPtr) + 1);
  62.         END;
  63.     END;
  64.  
  65. {--------------------------------------------------------------------------------------------------}
  66. {$S MAUtilitiesRes}
  67.  
  68. FUNCTION CanWriteLn: Boolean;
  69.  
  70.     BEGIN
  71.     {$IFC qDebug}
  72.     CanWriteLn := DebugCanWriteLn;
  73.     {$ELSEC}
  74.     CanWriteLn := FALSE;
  75.     {$ENDC}
  76.     END;
  77.  
  78. {--------------------------------------------------------------------------------------------------}
  79. {$S MAUtilitiesRes}
  80.  
  81. FUNCTION CanReadLn: Boolean;
  82.  
  83.     BEGIN
  84.     {$IFC qDebug}
  85.     CanReadLn := DebugCanReadLn;
  86.     {$ELSEC}
  87.     CanReadLn := FALSE;
  88.     {$ENDC}
  89.     END;
  90.  
  91. {--------------------------------------------------------------------------------------------------}
  92. {$Push}
  93. {$MC68020-}                                             { Need to be able to alert user if this
  94.                                                          isn't a 68020 machine }
  95. {$S MAUtilitiesRes}                                     { This must always be in a resident segment
  96.                                                          as aRect may be within a handle }
  97.  
  98. PROCEDURE CenterRectOnScreen(VAR aRect: Rect;
  99.                              horizontally, vertically, forDialog: Boolean);
  100.  
  101.     VAR
  102.         screenSize:         Point;
  103.         rectSize:            Point;
  104.         newSize:            INTEGER;
  105.  
  106.     BEGIN
  107.     { Calculate screen size minus menu bar }
  108.     WITH screenBits.bounds DO
  109.         SetPt(screenSize, right - left, bottom - top - gMBarHeight);
  110.                                                         { ??? should we use the same algorithm
  111.                                                          as in TWindow.GetMaxIntersectedDevice }
  112.     WITH aRect DO
  113.         BEGIN
  114.         SetPt(rectSize, right - left, bottom - top);
  115.         IF horizontally THEN
  116.             left := (screenSize.h - rectSize.h) DIV 2;
  117.         IF vertically THEN
  118.             IF forDialog THEN
  119.                 BEGIN
  120.                 newSize := (screenSize.v - rectSize.v) DIV 5;
  121.                 top := Max(newSize, 10) + gMBarHeight;
  122.                 END
  123.             ELSE
  124.                 top := (screenSize.v - rectSize.v) DIV 2;
  125.  
  126.         right := left + rectSize.h;
  127.         bottom := top + rectSize.v;
  128.         END;
  129.     END;
  130. {$Pop}
  131.  
  132. {--------------------------------------------------------------------------------------------------}
  133. {$S MAFile}
  134.  
  135. FUNCTION CloseFile(dataRefnum, rsrcRefnum: INTEGER): OSErr;
  136.  
  137.     VAR
  138.         err:                OSErr;
  139.  
  140.     BEGIN
  141.     err := noErr;
  142.  
  143.     IF dataRefnum <> kNoFileRefnum THEN
  144.         err := FSClose(dataRefnum);
  145.  
  146.     IF rsrcRefnum <> kNoFileRefnum THEN
  147.         BEGIN
  148.         CloseResFile(rsrcRefnum);
  149.         IF err = noErr THEN
  150.             err := ResError;
  151.         END;
  152.  
  153.     CloseFile := err;
  154.     END;
  155.  
  156. {--------------------------------------------------------------------------------------------------}
  157. {$S MAUtilitiesRes}
  158.  
  159. FUNCTION CompareStrings(first, second: Str255): INTEGER;
  160.  
  161. {$IFC NOT qNeedsROM128k}
  162.     EXTERNAL;
  163. {$ELSEC}
  164.  
  165. BEGIN
  166. CompareStrings := RelString(first, second, TRUE, TRUE);
  167. END;
  168. {$ENDC}
  169.  
  170. {--------------------------------------------------------------------------------------------------}
  171. {$Push}
  172. {$MC68020-}
  173. {$S MAUtilitiesRes}
  174.  
  175. FUNCTION ConcatNumber(aString: Str255;
  176.                       aNumber: longint): Str255;
  177.  
  178.     VAR
  179.         numberString:        Str255;
  180.  
  181.     BEGIN
  182.     NumToString(aNumber, numberString);
  183.     ConcatNumber := CONCAT(aString, numberString);
  184.     END;
  185. {$Pop}
  186.  
  187. {--------------------------------------------------------------------------------------------------}
  188. {$S MAFields}
  189.  
  190. PROCEDURE ConfigRecFields(aTitle: Str255;
  191.                           VAR aConfigRec: ConfigRecord;
  192.                           PROCEDURE DoToField(fieldName: Str255;
  193.                                               fieldAddr: Ptr;
  194.                                               fieldType: INTEGER));
  195.  
  196.     CONST
  197.         envSE30             = 7;                        { Not in the MPW 3.0 interfaces }
  198.  
  199.     VAR
  200.         aString:            Str255;
  201.  
  202.     BEGIN
  203.     DoToField(aTitle, NIL, bTitle);
  204.     DoToField('  environsVersion', @aConfigRec.environsVersion, bInteger);
  205.  
  206.     CASE aConfigRec.machineType OF
  207.         envMac:
  208.             aString := 'envMac';
  209.         envXL:
  210.             aString := 'envXL';
  211.         envMachUnknown:
  212.             aString := 'envMachUnknown';
  213.         env512KE:
  214.             aString := 'env512KE';
  215.         envMacPlus:
  216.             aString := 'envMacPlus';
  217.         envSE:
  218.             aString := 'envSE';
  219.         envMacII:
  220.             aString := 'envMacII';
  221.         envMacIIx:
  222.             aString := 'envMacIIx';
  223.         envSE30:
  224.             aString := 'envSE30';
  225.         OTHERWISE
  226.             aString := 'envMachUnknown';
  227.     END;
  228.     DoToField('  machineType', @aString, bString);
  229.  
  230.     DoToField('  systemVersion', @aConfigRec.systemVersion, bHexInteger);
  231.  
  232.     CASE aConfigRec.processor OF
  233.         envCPUUnknown:
  234.             aString := 'envCPUUnknown';
  235.         env68000:
  236.             aString := 'env68000';
  237.         env68010:
  238.             aString := 'env68010';
  239.         env68020:
  240.             aString := 'env68020';
  241.         env68030:
  242.             aString := 'env68030';
  243.         OTHERWISE
  244.             aString := 'envCPUUnknown';
  245.     END;
  246.     DoToField('  processor', @aString, bString);
  247.  
  248.     DoToField('  hasFPU', @aConfigRec.hasFPU, bBoolean);
  249.     DoToField('  hasColorQD', @aConfigRec.hasColorQD, bBoolean);
  250.  
  251.     CASE aConfigRec.keyboardType OF
  252.         envUnknownKbd:
  253.             aString := 'envUnknownKbd';
  254.         envMacKbd:
  255.             aString := 'envMacKbd';
  256.         envMacAndPad:
  257.             aString := 'envMacAndPad';
  258.         envMacPlusKbd:
  259.             aString := 'envMacPlusKbd';
  260.         envAExtendKbd:
  261.             aString := 'envAExtendKbd';
  262.         envStandADBKbd:
  263.             aString := 'envStandADBKbd';
  264.         OTHERWISE
  265.             aString := 'envUnknownKbd';
  266.     END;
  267.     DoToField('  keyboardType', @aString, bString);
  268.  
  269.     DoToField('  atDrvrVersNum', @aConfigRec.atDrvrVersNum, bInteger);
  270.     DoToField('  sysVRefNum', @aConfigRec.sysVRefNum, bInteger);
  271.     DoToField('  hasROM128K', @aConfigRec.hasROM128K, bBoolean);
  272.     DoToField('  hasHFS', @aConfigRec.hasHFS, bBoolean);
  273.     DoToField('  hasHierarchicalMenus', @aConfigRec.hasHierarchicalMenus, bBoolean);
  274.     DoToField('  hasScriptManager', @aConfigRec.hasScriptManager, bBoolean);
  275.     DoToField('  hasStyleTextEdit', @aConfigRec.hasStyleTextEdit, bBoolean);
  276.     DoToField('  hasSoundManager', @aConfigRec.hasSoundManager, bBoolean);
  277.     DoToField('  hasWaitNextEvent', @aConfigRec.hasWaitNextEvent, bBoolean);
  278.     DoToField('  hasSCSI', @aConfigRec.hasSCSI, bBoolean);
  279.     DoToField('  hasDesktopBus', @aConfigRec.hasDesktopBus, bBoolean);
  280.     DoToField('  hasAUX', @aConfigRec.hasAUX, bBoolean);
  281.     DoToField('  hasTempMem', @aConfigRec.hasTempMem, bBoolean);
  282.     DoToField('  has32BitQD', @aConfigRec.has32BitQD, bBoolean);
  283.     END;
  284.  
  285. {--------------------------------------------------------------------------------------------------}
  286. {$S MAUtilitiesRes}
  287.  
  288. PROCEDURE CopyStr255(VAR fmStr: Str255;
  289.                      toAddr: UNIV Ptr);
  290.  
  291.     BEGIN
  292.     BlockMove(@fmStr, toAddr, LENGTH(fmStr) + 1);
  293.     END;
  294.  
  295. {--------------------------------------------------------------------------------------------------}
  296. {$S MAUtilitiesRes}
  297.  
  298. PROCEDURE DefaultSize(VAR theSize: INTEGER);
  299.  
  300.     BEGIN
  301.     IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  302.         BEGIN
  303.         IF theSize = GetDefFontSize THEN
  304.             theSize := 0;
  305.         END
  306.     ELSE IF qNeedsROM128K | gConfiguration.hasROM128K THEN
  307.         BEGIN
  308.         IF (theSize = IntegerPtr(kLMSysFontSize)^) THEN
  309.             theSize := 0;
  310.         END
  311.     ELSE IF theSize = 12 THEN                            { Guess }
  312.         theSize := 0;
  313.     END;
  314.  
  315. {--------------------------------------------------------------------------------------------------}
  316. {$S MAFile}
  317.  
  318. FUNCTION DeleteFile(namePtr: StringPtr;
  319.                     volRefnum: INTEGER): OSErr;
  320.  
  321.     VAR
  322.         hPB:                HParamBlockRec;
  323.         err:                OSErr;
  324.  
  325.     BEGIN
  326.     WITH hPB DO
  327.         BEGIN
  328.         ioNamePtr := namePtr;
  329.         ioVRefnum := volRefnum;
  330.         ioFVersNum := 0;
  331.         END;
  332.  
  333.     err := FillInDirID(@hPB);                            {to avoid PMSP}
  334.  
  335.     IF err = noErr THEN
  336.         err := PBHDelete(@hPB, FALSE);
  337.  
  338.     DeleteFile := err;
  339.     END;
  340.  
  341. {--------------------------------------------------------------------------------------------------}
  342. {$S MAUtilitiesRes}
  343.  
  344. PROCEDURE DisposIfHandle(aHandle: UNIV Handle);
  345.  
  346.     BEGIN
  347.     aHandle := DisposeIfHandle(aHandle);
  348.     END;
  349.  
  350. {--------------------------------------------------------------------------------------------------}
  351. {$S MAUtilitiesRes}
  352.  
  353. FUNCTION DisposeIfHandle(aHandle: UNIV Handle): Handle;
  354.  
  355.     CONST
  356.         resourceBit         = 5;
  357.         initVal             = $D3;                        { odd at all byte boundaries }
  358.  
  359.     VAR
  360.         handleBits:         SignedByte;
  361.  
  362.     BEGIN
  363.     DisposeIfHandle := NIL;                             { For convenience of caller }
  364.  
  365.     IF aHandle <> NIL THEN
  366.         BEGIN
  367.         IF qDebug THEN
  368.             BEGIN
  369.             { Test handlehood }
  370.             IF IsHandle(aHandle) THEN
  371.                 BEGIN
  372.                 handleBits := GetHandleBits(aHandle);
  373.                 IF MemError <> noErr THEN
  374.                     BEGIN
  375.                     WriteLn('Handle was so bad I couldn''t even get the handle bits!');
  376.                     WrLblHexLongint('Bad Handle', longint(aHandle));
  377.                     WriteLn;
  378.                     ProgramBreak('');
  379.                     END
  380.                 ELSE IF IsHandlePurged(aHandle) THEN    { h might have been purged }
  381.                     BEGIN
  382.                     DisposHandle(aHandle);
  383.                     END
  384.                 ELSE IF BTST(handleBits, resourceBit) THEN
  385.                     BEGIN
  386.                     WriteLn('Trying to dispose a resource handle');
  387.                     WrLblHexLongint('Bad Handle', longint(aHandle));
  388.                     WriteLn;
  389.                     ProgramBreak('');
  390.                     END
  391.                 ELSE
  392.                     BEGIN
  393.                     { Set the handle contents to a real nice value for any dangling pointerciples }
  394.                     BlockSet(aHandle^, GetHandleSize(aHandle), initVal);
  395.                     DisposHandle(aHandle);
  396.                     END;
  397.                 END
  398.             ELSE
  399.                 BEGIN
  400.                 IF VerboseIsHandle(aHandle) THEN;        { Get the diagnosis printed }
  401.                 WriteLn('Trying to dispose an invalid handle');
  402.                 WrLblHexLongint('Bad Handle', longint(aHandle));
  403.                 WriteLn;
  404.                 ProgramBreak('');
  405.                 END;
  406.             END
  407.         ELSE
  408.             DisposHandle(aHandle);
  409.         END;
  410.     END;
  411.  
  412. {--------------------------------------------------------------------------------------------------}
  413. {$S MAUtilitiesRes}
  414.  
  415. PROCEDURE DisposIfPtr(aPtr: UNIV Ptr);
  416.  
  417.     BEGIN
  418.     aPtr := DisposeIfPtr(aPtr);
  419.     END;
  420.  
  421. {--------------------------------------------------------------------------------------------------}
  422. {$S MAUtilitiesRes}
  423.  
  424. FUNCTION DisposeIfPtr(aPtr: UNIV Ptr): Ptr;
  425.  
  426.     CONST
  427.         resourceBit         = 5;
  428.         initVal             = $D5;                        { odd at all byte boundaries }
  429.  
  430.     BEGIN
  431.     DisposeIfPtr := NIL;                                { For convenience of caller }
  432.  
  433.     IF aPtr <> NIL THEN
  434.         BEGIN
  435.         IF qDebug THEN
  436.             BEGIN
  437.             { Test pointerhood, ??? Shouldn't we have a real test here? }
  438.             IF (NOT Odd(Ord(aPtr))) THEN
  439.                 BEGIN
  440.                 BlockSet(aPtr, GetPtrSize(aPtr), initVal);
  441.                 DisposPtr(aPtr);
  442.                 END
  443.             ELSE
  444.                 BEGIN
  445.                 WriteLn('Trying to dispose an invalid pointer');
  446.                 WrLblHexLongint('Bad Pointer', longint(aPtr));
  447.                 WriteLn;
  448.                 ProgramBreak('');
  449.                 END;
  450.             END
  451.         ELSE
  452.             DisposPtr(aPtr);
  453.         aPtr := NIL;
  454.         END;
  455.     END;
  456.  
  457. {--------------------------------------------------------------------------------------------------}
  458.  
  459. FUNCTION EqualBlocks(first, second: UNIV Ptr;
  460.                      theSize: INTEGER): Boolean;
  461.     EXTERNAL;
  462.  
  463. {--------------------------------------------------------------------------------------------------}
  464. {$S MAUtilitiesRes}
  465.  
  466. PROCEDURE EachWMgrWindowDo(PROCEDURE DoToWMgrWindow(theWMgrWindow: WindowPtr));
  467.  
  468.     VAR
  469.         aWindowPtr:         WindowPtr;
  470.  
  471.     BEGIN
  472.     aWindowPtr := GetWindowList;
  473.     WHILE (aWindowPtr <> NIL) DO
  474.         BEGIN
  475.         IF (aWindowPtr <> gWorkPort) THEN        { ignore the work window }
  476.             DoToWMgrWindow(aWindowPtr);
  477.         aWindowPtr := WindowPtr(WindowPeek(aWindowPtr)^.nextWindow);
  478.         END;
  479.     END;
  480.  
  481. {--------------------------------------------------------------------------------------------------}
  482. {$S MAUtilitiesRes}
  483.  
  484. FUNCTION FindWindowBefore(theWindow: WindowPtr): WindowPtr;
  485. { returns the window just before a given window.  Returns nil if the given window is frontmost or
  486.   not found. }
  487.  
  488.     PROCEDURE DoToWMgrWindow(theWMgrWindow: WindowPtr);
  489.  
  490.         BEGIN
  491.         IF WindowPtr(WindowPeek(theWMgrWindow)^.nextWindow) = theWindow THEN
  492.             BEGIN
  493.             FindWindowBefore := theWMgrWindow;
  494.             exit(FindWindowBefore);
  495.             END;
  496.         END;
  497.  
  498.     BEGIN
  499.     FindWindowBefore := NIL;
  500.     EachWMgrWindowDo(DoToWMgrWindow);
  501.     END;
  502.  
  503. {--------------------------------------------------------------------------------------------------}
  504. {$S MAFile}
  505.  
  506. FUNCTION FileModDate(name: Str255;
  507.                      volRefnum: INTEGER): longint;
  508.  
  509.     VAR
  510.         pb:                 HParamBlockRec;
  511.  
  512.     BEGIN
  513.     IF GetFileInfo(name, volRefnum, pb) = noErr THEN
  514.         FileModDate := pb.ioFlMdDat
  515.     ELSE
  516.         FileModDate := 0;
  517.     END;
  518.  
  519. {--------------------------------------------------------------------------------------------------}
  520.  
  521. PROCEDURE FieldToString(theData: Ptr;
  522.                         fieldType: INTEGER;
  523.                         VAR theString: Str255);
  524.     EXTERNAL;
  525.  
  526. {--------------------------------------------------------------------------------------------------}
  527. {$S MAFile}
  528.  
  529. FUNCTION FillInDirID(pb: HParmBlkPtr): OSErr;
  530.  
  531.     BEGIN
  532.     FillInDirID := GetDirID(pb^.ioVRefnum, pb^.ioDirID);
  533.     END;
  534.  
  535. {--------------------------------------------------------------------------------------------------}
  536. {$S MAUtilitiesRes}
  537.  
  538. FUNCTION GetActualJustification(justification: INTEGER): INTEGER;
  539.  
  540.     BEGIN
  541.     IF justification = teJustSystem THEN                { actually teJustLeft }
  542.         BEGIN
  543.         IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  544.             GetActualJustification := GetSysJust
  545.         ELSE IF qNeedsROM128K | gConfiguration.hasROM128K THEN
  546.             GetActualJustification := IntegerPtr(kLMTESysJust)^
  547.         ELSE
  548.             GetActualJustification := teJustLeft;
  549.         END
  550.     ELSE
  551.         GetActualJustification := justification;
  552.     END;
  553.  
  554. {--------------------------------------------------------------------------------------------------}
  555. {$S MAFile}
  556.  
  557. FUNCTION GetDirID(VAR vRefnum: INTEGER;
  558.                   VAR dirID: longint): OSErr;
  559.  
  560.     VAR
  561.         pb:                 WDPBRec;
  562.  
  563.     BEGIN
  564.     IF qNeedsROM128K | gConfiguration.hasHFS THEN
  565.         BEGIN
  566.         WITH pb DO
  567.             BEGIN
  568.             ioNamePtr := NIL;
  569.             ioVRefnum := vRefnum;
  570.             ioWDIndex := 0;
  571.             ioWDProcID := 0;
  572.             ioWDVRefnum := vRefnum;
  573.             END;
  574.         GetDirID := PBGetWDInfo(@pb, FALSE);
  575.         vRefnum := pb.ioWDVRefnum;
  576.         dirID := pb.ioWDDirID;
  577.         END
  578.     ELSE
  579.         BEGIN
  580.         dirID := 0;
  581.         GetDirID := noErr;
  582.         END;
  583.     END;
  584.  
  585. {--------------------------------------------------------------------------------------------------}
  586. {$S MAFile}
  587.  
  588. FUNCTION GetFileInfo(name: Str255;
  589.                      volRefnum: INTEGER;
  590.                      VAR info: HParamBlockRec): OSErr;
  591.  
  592.     VAR
  593.         err:                OSErr;
  594.  
  595.     BEGIN
  596.     WITH info DO
  597.         BEGIN
  598.         ioNamePtr := @name;
  599.         ioVRefnum := volRefnum;
  600.         ioFVersNum := 0;
  601.         ioFDirIndex := 0;
  602.         END;
  603.     err := FillInDirID(@info);
  604.     IF err = noErr THEN
  605.         err := PBHGetFInfo(@info, FALSE);
  606.     GetFileInfo := err;
  607.     END;
  608.  
  609. {--------------------------------------------------------------------------------------------------}
  610. {$S MAUtilitiesRes}
  611.  
  612. FUNCTION GetFontNum(fontName: Str255): INTEGER;
  613.  
  614.     VAR
  615.         fontNum:            INTEGER;
  616.  
  617.     BEGIN
  618.     UprString(fontName, FALSE);
  619.     IF fontName = kSysFontName THEN
  620.         BEGIN
  621.         IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  622.             fontNum := GetSysFont
  623.         ELSE
  624.             fontNum := systemFont;
  625.         END
  626.     ELSE IF fontName = kApplFontName THEN
  627.         BEGIN
  628.         IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  629.             fontNum := GetAppFont
  630.         ELSE
  631.             fontNum := applFont;
  632.         END
  633.     ELSE
  634.         GetFNum(fontName, fontNum);
  635.     GetFontNum := fontNum;
  636.     END;
  637.  
  638. {--------------------------------------------------------------------------------------------------}
  639. {$S MAUtilitiesRes}                                     {Must be in Main segment and cannot call to
  640.                                                          any other segment.}
  641.  
  642. FUNCTION GetHandleBits(h: Handle): SignedByte;
  643.  
  644.     CONST
  645.         MemErr = $220;            {[GLOBAL VAR]  last memory manager error [word]}
  646.  
  647.     BEGIN
  648.     IF qNeedsROM128K | gConfiguration.hasROM128K THEN
  649.         GetHandleBits := HGetState(h)
  650.     ELSE
  651.         BEGIN
  652.         IntegerPtr(MemErr)^ := noErr;
  653.         IF (h=nil) THEN
  654.             GetHandleBits := 0
  655.         ELSE
  656.             GetHandleBits := SignedBytePtr(h)^;
  657.         END;
  658.     END;
  659.  
  660. {--------------------------------------------------------------------------------------------------}
  661. {$IFC NOT qNeedsColorQD}                                { Becomes an inline if we know the machine
  662.                                                          has color QD }
  663. {$S MAUtilitiesRes}
  664.  
  665. PROCEDURE GetIfBkColor(VAR aColor: RGBColor);
  666.  
  667.     CONST
  668.         BlackBit            = 5;
  669.         YellowBit            = 6;
  670.         MagentaBit            = 7;
  671.         CyanBit             = 8;
  672.  
  673.     VAR
  674.         oldColor:            longint;
  675.  
  676.     BEGIN
  677.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  678.         GetBackColor(aColor)
  679.     ELSE
  680.         BEGIN                                            { Map old, dumb CMYB system to RGB color }
  681.         {[f-]}
  682.         (*                        xxxxxxx C.MY B rgb w b    = RGB
  683.         blackColor        =  33 = 0000000 0.00 1 000 0 1    = 000
  684.         whiteColor        =  30 = 0000000 0.00 0 111 1 0    = 111
  685.         redColor        = 205 = 0000000 0.11 0 011 0 1    = 100
  686.         greenColor        = 341 = 0000000 1.01 0 101 0 1    = 010
  687.         blueColor        = 409 = 0000000 1.10 0 110 0 1    = 001
  688.         cyanColor        = 273 = 0000000 1.00 0 100 0 1    = 011
  689.         magentaColor    = 137 = 0000000 0.10 0 010 0 1    = 101
  690.         yellowColor     =  69 = 0000000 0.01 0 001 0 1    = 110
  691.         *)
  692.         {[f+]}
  693.  
  694.         oldColor := thePort^.bkColor;                    { Fetch old color }
  695.         aColor := gRGBBlack;                            { Prime returned color to black }
  696.         IF BTST(oldColor, BlackBit) THEN                { If color isn't black, force CMY = 111 }
  697.             oldColor := BOR(oldColor, $1C0);
  698.         IF NOT BTST(oldColor, CyanBit) THEN             { Absence of cyan = presence of red }
  699.             aColor.red := $FFFF;
  700.         IF NOT BTST(oldColor, MagentaBit) THEN            { Absence of magenta = presence of green }
  701.             aColor.green := $FFFF;
  702.         IF NOT BTST(oldColor, YellowBit) THEN            { Absence of yellow = presence of blue }
  703.             aColor.blue := $FFFF;
  704.         END;
  705.     END;
  706. {$ENDC}
  707.  
  708. {--------------------------------------------------------------------------------------------------}
  709. {$IFC NOT qNeedsColorQD}                                { Becomes an inline if we know the machine
  710.                                                          has color QD }
  711. {$S MAUtilitiesRes}
  712.  
  713. PROCEDURE GetIfColor(VAR aColor: RGBColor);
  714.  
  715.     CONST
  716.         BlackBit            = 5;
  717.         YellowBit            = 6;
  718.         MagentaBit            = 7;
  719.         CyanBit             = 8;
  720.  
  721.     VAR
  722.         oldColor:            longint;
  723.  
  724.     BEGIN
  725.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  726.         GetForeColor(aColor)
  727.     ELSE
  728.         BEGIN                                            { Map old, dumb CMYB system to RGB color }
  729.   {   xxxxxxx C.MY B rgb w b = RGB
  730.   blackColor  =  33 = 0000000 0.00 1 000 0 1 = 000
  731.   whiteColor  =  30 = 0000000 0.00 0 111 1 0 = 111
  732.   redColor = 205 = 0000000 0.11 0 011 0 1 = 100
  733.   greenColor  = 341 = 0000000 1.01 0 101 0 1 = 010
  734.   blueColor  = 409 = 0000000 1.10 0 110 0 1 = 001
  735.   cyanColor  = 273 = 0000000 1.00 0 100 0 1 = 011
  736.   magentaColor = 137 = 0000000 0.10 0 010 0 1 = 101
  737.   yellowColor  =  69 = 0000000 0.01 0 001 0 1 = 110
  738.   }
  739.         oldColor := thePort^.fgColor;                    { Fetch old color }
  740.         aColor := gRGBBlack;                            { Prime returned color to black }
  741.         IF BTST(oldColor, BlackBit) THEN                { If color isn't black, force CMY = 111 }
  742.             oldColor := BOR(oldColor, $1C0);
  743.         IF NOT BTST(oldColor, CyanBit) THEN             { Absence of cyan = presence of red }
  744.             aColor.red := $FFFF;
  745.         IF NOT BTST(oldColor, MagentaBit) THEN            { Absence of magenta = presence of green }
  746.             aColor.green := $FFFF;
  747.         IF NOT BTST(oldColor, YellowBit) THEN            { Absence of yellow = presence of blue }
  748.             aColor.blue := $FFFF;
  749.         END;
  750.     END;
  751. {$ENDC}
  752.  
  753. {--------------------------------------------------------------------------------------------------}
  754. {$S MAUtilitiesRes}
  755.  
  756. PROCEDURE GetPortFontInfo(fontNum: INTEGER;
  757.                           VAR fontName: Str255;
  758.                           VAR fontSize: INTEGER);
  759.  
  760.     BEGIN
  761.     IF (fontNum = systemFont) | ((qNeedsROM128K | gConfiguration.hasROM128K) & (
  762.        (qNeedsScriptManager | gConfiguration.hasScriptManager) & (fontNum = GetSysFont)) |
  763.        (fontNum = IntegerPtr(kLMSysFontFam)^)) THEN
  764.         BEGIN
  765.         fontName := kSysFontName;
  766.         DefaultSize(fontSize);
  767.         END
  768.  
  769.     ELSE IF (fontNum = applFont) | (((qNeedsScriptManager | gConfiguration.hasScriptManager) &
  770.             (fontNum = GetAppFont)) | (fontNum = IntegerPtr(kLMApFontID)^)) THEN
  771.         BEGIN
  772.         fontName := kApplFontName;
  773.         DefaultSize(fontSize);
  774.         END
  775.  
  776.     ELSE
  777.         GetFontName(fontNum, fontName);
  778.     END;
  779.  
  780. {--------------------------------------------------------------------------------------------------}
  781. {$Push}
  782. {$MC68020-}
  783. {$S Main}
  784.  
  785. PROCEDURE LockHandleHigh(h: Handle);
  786.  
  787.     BEGIN
  788.     IF h <> NIL THEN
  789.         BEGIN
  790.         IF qDebug & NOT IsHandle(h) THEN
  791.             BEGIN
  792.             IF VerboseIsHandle(h) THEN;                 { Get the diagnosis printed }
  793.             ProgramBreak('In LockHandleHigh: not handed a handle');
  794.             END
  795.         ELSE
  796.             BEGIN
  797.             MoveHHi(h);                                 { ??? check MemErr ??? }
  798.             HLock(h);
  799.             END;
  800.         END;
  801.     END;
  802. {$Pop}
  803.  
  804. {--------------------------------------------------------------------------------------------------}
  805. {$Push}
  806. {$MC68020-}
  807. {$S MAUtilitiesRes}
  808.  
  809. FUNCTION GetTrapType(theTrap: INTEGER): TrapType;
  810.  
  811.     BEGIN
  812.     { OS traps start with A0, Tool with A8 or AA. }
  813.     IF BAND(theTrap, $0800) = 0 THEN                    { per D.A }
  814.         GetTrapType := OSTrap
  815.     ELSE
  816.         GetTrapType := ToolTrap;
  817.     END;
  818. {$Pop}
  819.  
  820. {--------------------------------------------------------------------------------------------------}
  821. { Nothing in this procedure can be allowed to fail }
  822. {$Push}
  823. {$MC68020-}
  824. {$S MAMiniInit}
  825.  
  826. PROCEDURE DoRealInitToolBox;
  827.  
  828.     VAR
  829.         aCursHandle:        CursHandle;
  830.  
  831.     BEGIN
  832.     InitGraf(@thePort);
  833.     InitFonts;
  834.     InitWindows;                                        { creates non-relocatable for the WM port }
  835.  
  836.     { _DON'T_ flush disk-inserted or MultiFinder™ events or you'll be sorry! }
  837.     FlushEvents(everyEvent - diskMask - app4Mask, 0);
  838.  
  839.     InitMenus;
  840.     TEInit;
  841.     InitDialogs(NIL);
  842.     aCursHandle := GetCursor(watchCursor);                { Watch should be in system file, but just
  843.                                                          in case… }
  844.  
  845.     InitCursor;                                         { !!! This forces an arrow cursor. Is there
  846.                                                          a way to reset the show/hide level and
  847.                                                          init all the cursor goo without having
  848.                                                          this visual glitch? ( the Finder™ sets the
  849.                                                          cursor to a watch when launching. It would
  850.                                                          be nice to stay that way until the app is
  851.                                                          ready for events. }
  852.     IF aCursHandle <> NIL THEN
  853.         SetCursor(aCursHandle^^);                        { Change cursor to watch }
  854.  
  855.     {$IFC qDebug}                                        { Enable pre and postcondition testing }
  856.     gPreCondition := TRUE;
  857.     gPostCondition := TRUE;
  858.     {$ENDC}
  859.  
  860.     { Find out just what kind of environment we're dealing with here }
  861.     DefineConfiguration(gConfiguration);
  862.  
  863.     { Init the stuff that MATextBox uses }
  864.     gMATextBoxTE := NIL;
  865.     gTEDefaultWordBreak := NIL;
  866.  
  867.     SetRGBColor(gRGBBlack, 0, 0, 0);
  868.     SetRGBColor(gRGBWhite, $FFFF, $FFFF, $FFFF);
  869.  
  870.     { -1 = $FFFFFFFF, the largest 32 bit address.  Our routine StripLong uses a pre-stripped
  871.     address gStrippedAddress to avoid the yucky MPW glue. }
  872.     gStrippedAddress := StripAddress(Ptr( - 1));
  873.  
  874.     { !!! I hate to have to allocate this memory here.    Is there a better way to encapsulate
  875.     this and defer the allocation until later.    Many routines touch the region (Even after
  876.     InvalidateCursor was implemented) }
  877.     gCursorRgn := NewRgn;                                { Hope it doesn't fail. Really isn't likely
  878.                                                          to though. }
  879.  
  880.     { Ensure that the following tests for the script manager or 128K ROM are *always* performed
  881.     since we may have launched on a non-script manager Mac or a non-128K Mac *even*
  882.     if app is built with -NeedsScriptManager or -NeedsROM128K }
  883.  
  884.     IF {qNeedsScriptManager |} gConfiguration.hasScriptManager THEN
  885.         gMBarHeight := GetMBarHeight
  886.     ELSE IF {qNeedsROM128K |} gConfiguration.hasROM128K THEN
  887.         gMBarHeight := GetLMMBarHeight
  888.     ELSE
  889.         gMBarHeight := 20;                                { Guess }
  890.  
  891.     {$IFC qDebug OR qInspector}
  892.     gFieldToStrRtn := @StdFieldToString;
  893.     {$EndC}
  894.  
  895.     gBoolString[TRUE] := 'TRUE';
  896.     gBoolString[FALSE] := 'FALSE';
  897.     gDeadStripSuppression := FALSE;
  898.     gCreateWithTemplates := gDeadStripSuppression;        { for compatibility with Dave W. class notes
  899.                                                          }
  900.     { The refnum where the application's resources should be found }
  901.     gApplicationRefNum := CurResFile;
  902.  
  903.     gToolBoxInitialized := TRUE;
  904.     END;
  905. {$Pop}
  906.  
  907. {--------------------------------------------------------------------------------------------------}
  908. { Nothing in this procedure can be allowed to fail }
  909. {$Push}
  910. {$MC68020-}
  911. {$S Main}                                                { This procedure is intended to be in "Main"
  912.                                                          which is already loaded }
  913.  
  914. PROCEDURE _DataInit;                                    { Routine in the A5 globals initializer }
  915.     EXTERNAL;
  916.  
  917. PROCEDURE InitToolBox;
  918.  
  919.     CONST
  920.         kBreathingRoom        = 1024;                     { Amount of heap space needed for init }
  921.  
  922.     VAR
  923.         h:                    Handle;
  924.  
  925.     PROCEDURE FailedInitToolBox;
  926.  
  927.         BEGIN
  928.         IF qDebug THEN
  929.             DebugStr('Not enough room to init ToolBox Managers');
  930.         ExitToShell;                                    {??? any good way to signal this to the user
  931.                                                          ???}
  932.         END;
  933.  
  934.     BEGIN
  935.     { the heap and stack don't overlap. So there's enough room to init the managers.
  936.     Make sure that the MAMiniInit Segment can be loaded and that there's still a little
  937.     Room after that. }
  938.  
  939.     UnloadSeg(@_DataInit);                                { Toss some ballast }
  940.  
  941.     { "MAMain" this is MacApp's own code that must be resident… even before/during the UMemory startup.
  942.     GetNamedResource will call RsrvMem which locates the handle as low in memory as possible.
  943.     We will then lock it there… just like "Main"}
  944.     SetResLoad(FALSE);
  945.     h := GetNamedResource('CODE', 'MAMain');
  946.     SetResLoad(TRUE);
  947.     IF (h <> NIL) THEN
  948.         ResrvMem(SizeResource(h));
  949.     h := GetNamedResource('CODE', 'MAMain');
  950.     IF (h <> NIL) THEN
  951.         HLock(h)
  952.     ELSE
  953.         FailedInitToolBox;
  954.  
  955.     h := GetNamedResource('CODE', 'MAMiniInit');
  956.     IF (h <> NIL) THEN
  957.         HLock(h)
  958.     ELSE
  959.         FailedInitToolBox;
  960.  
  961.     { Attempt to ensure that there is going to be kBreathingRoom bytes available in the heap so that
  962.     when the actual toolbox managers are initialized there is a significantly reduced chance that
  963.     they will express their displeasure with us through SysErr -25 or -2.  If the space is not
  964.     currently available in the zone as shown by FreeMem then attempting to allocate it will let
  965.     growzoneproc operate and grow the zone a little, as necessary.  If, after that, we haven't been
  966.     able to get the breathing room we desire then just give up and fade silently away. (Like the old
  967.     soldier, not the old executive). }
  968.  
  969.     IF FreeMem >= kBreathingRoom THEN
  970.         DoRealInitToolBox
  971.     ELSE
  972.         BEGIN
  973.         h := NewHandle(kBreathingRoom);
  974.         IF h <> NIL THEN                             { get the grow space }
  975.             BEGIN
  976.             DisposHandle(h);
  977.             DoRealInitToolBox;
  978.             END
  979.         ELSE
  980.             FailedInitToolBox;                        { Give up }
  981.         END;
  982.     END;
  983. {$Pop}
  984.  
  985. {--------------------------------------------------------------------------------------------------}
  986. { Nothing in this procedure can be allowed to fail }
  987. {$Push}
  988. {$MC68020-}
  989. {$S MAMiniInit}
  990.  
  991. FUNCTION ValidateConfiguration(configuration: ConfigRecord): Boolean;
  992.  
  993.     VAR
  994.         isSupported:        Boolean;
  995.  
  996.     BEGIN
  997.     { Run the gauntlet of support tests using the conditionally set constants.
  998.     If any single test fails then the app is considered unsupported on this machine.  }
  999.  
  1000.     isSupported := TRUE;
  1001.  
  1002.     IF qNeedsScriptManager THEN
  1003.         isSupported := isSupported & configuration.hasScriptManager;
  1004.  
  1005.     IF qNeedsROM128K THEN
  1006.         isSupported := isSupported & configuration.hasROM128K;
  1007.  
  1008.     IF qNeedsHierarchicalMenus THEN
  1009.         isSupported := isSupported & configuration.hasHierarchicalMenus;
  1010.  
  1011.     IF qNeedsStyleTextEdit THEN
  1012.         isSupported := isSupported & configuration.hasStyleTextEdit;
  1013.  
  1014.     IF qNeedsWaitNextEvent THEN
  1015.         isSupported := isSupported & configuration.hasWaitNextEvent;
  1016.  
  1017.     IF qNeedsColorQD THEN
  1018.         isSupported := isSupported & configuration.hasColorQD;
  1019.  
  1020.     IF qNeedsMC68020 THEN
  1021.         isSupported := isSupported & ((configuration.processor <> env68000) &
  1022.                        (configuration.processor <> env68010));
  1023.  
  1024.     IF qNeedsMC68030 THEN
  1025.         isSupported := isSupported & ((configuration.processor <> env68000) &
  1026.                        (configuration.processor <> env68010) & (configuration.processor <>
  1027.                        env68020));
  1028.  
  1029.     IF qNeedsFPU THEN
  1030.         isSupported := isSupported & configuration.hasFPU;
  1031.  
  1032.     { skanky hack under A/UX to ensure that all app's are pulled to front early on }
  1033.     IF configuration.hasAUX THEN
  1034.         PullApplicationToFront;
  1035.  
  1036.     ValidateConfiguration := isSupported;
  1037.     END;
  1038. {$Pop}
  1039.  
  1040. {--------------------------------------------------------------------------------------------------}
  1041. { Nothing in this procedure can be allowed to fail }
  1042. {$Push}
  1043. {$MC68020-}
  1044. {$S MAMiniInit}
  1045.  
  1046. PROCEDURE DefineConfiguration(VAR configuration: ConfigRecord);
  1047.  
  1048.     CONST
  1049.  
  1050.         {Masks for the HwCfgFlags}
  1051.         mSCSIPort            = $8000;
  1052.         mDesktopBus         = $0400;
  1053.         mHasAUX             = $0200;
  1054.  
  1055.         { Test that DTS says is OK for 32 bit QD.  It is an internal trap that is only implemented
  1056.         if QD32 is installed.    }
  1057.         _MA32BitQD            = $AB03;
  1058.  
  1059.     VAR
  1060.         kludge:             ^SysEnvRec;
  1061.         result:             OSErr;
  1062.  
  1063.     BEGIN
  1064.     kludge := @configuration;
  1065.     result := SysEnvirons(1, kludge^);                    {Version 1 shouldn't fail}
  1066.  
  1067.     WITH configuration DO
  1068.         BEGIN
  1069.         hasDesktopBus := BAND(GetHwCfgFlags, mDesktopBus) > 0;
  1070.         hasSCSI := BAND(GetHwCfgFlags, mSCSIPort) > 0;
  1071.         hasAUX := BAND(GetHwCfgFlags, mHasAUX) > 0;
  1072.         hasROM128K := machineType > envMac;
  1073.         IF hasROM128K THEN
  1074.             hasHFS := TRUE
  1075.         ELSE
  1076.             hasHFS := GetFSFCBLen > 0;
  1077.         hasHierarchicalMenus := hasROM128K & TrapExists(_PopUpMenuSelect);
  1078.         hasScriptManager := hasROM128K & TrapExists(_ScriptUtil);
  1079.         hasStyleTextEdit := systemVersion >= $600;
  1080.         hasSoundManager := hasROM128K & TrapExists(_SndDoCommand);
  1081.         hasWaitNextEvent := hasROM128K & TrapExists(_WaitNextEvent);
  1082.         hasTempMem := TrapExists(_OSDispatch);
  1083.         has32BitQD := TrapExists(_MA32BitQD);
  1084.         END;
  1085.     END;
  1086. {$Pop}
  1087.  
  1088. {--------------------------------------------------------------------------------------------------}
  1089. { Nothing in this procedure can be allowed to fail }
  1090. {$Push}
  1091. {$MC68020-}
  1092. {$S Main}                                                { Must be in main segment as it is called in
  1093.                                                          early initialization AND in MacAppAlert }
  1094.  
  1095. PROCEDURE PullApplicationToFront;
  1096.  
  1097.     VAR
  1098.         theEvent:            EventRecord;
  1099.         i:                    INTEGER;
  1100.  
  1101.     BEGIN
  1102.     { The "Programmer's guide to MultiFinder™ says make an event call several times.
  1103.     I guess 3 calls counts as several.    Also, it says call GetNextEvent but we don't
  1104.     want to lose events on the floor so we use EventAvail since it seems to work OK }
  1105.     FOR i := 1 TO 3 DO
  1106.         IF EventAvail(everyEvent, theEvent) THEN;
  1107.     END;
  1108. {$Pop}
  1109.  
  1110. {--------------------------------------------------------------------------------------------------}
  1111.  
  1112. {$S MAUtilitiesRes}
  1113.  
  1114. FUNCTION IsFreeHandle(h: UNIV Handle): Boolean;
  1115. { Walk the free-list looking for the given handle }
  1116.  
  1117.     VAR
  1118.         applZone:            THz;
  1119.         currHandle:         Handle;
  1120.  
  1121.     BEGIN
  1122.     IsFreeHandle := FALSE;
  1123.     applZone := ApplicZone;
  1124.     currHandle := Handle(applZone^.hFstFree);
  1125.     WHILE (currHandle <> NIL) DO
  1126.         BEGIN
  1127.         IF currHandle = h THEN
  1128.             BEGIN
  1129.             IsFreeHandle := TRUE;
  1130.             LEAVE;
  1131.             END;
  1132.         currHandle := Handle(currHandle^);
  1133.         END;
  1134.  
  1135.     END;
  1136.  
  1137. {--------------------------------------------------------------------------------------------------}
  1138.  
  1139. {$S MAUtilitiesRes}
  1140.  
  1141. FUNCTION TestRecoverHandle(masterPointer: Ptr;
  1142.                            h: UNIV Handle): Boolean;
  1143.  
  1144. { TestRecoverHandle determines if the given masterPointer recovers via RecoverHandle to be the given
  1145. handle h. Since RecoverHandle fails if h is from a heap other than the current heap, we need to set
  1146. the zone to be the handle's zone before calling RecoverHandle. }
  1147.  
  1148.     {$IFC FALSE}
  1149.     VAR
  1150.         itsZone,                                        { the handle's zone }
  1151.         currentZone:        THz;                        { the current zone (don't assume ApplicZone)
  1152.                                                          }
  1153.         restoreZone:        Boolean;                    { flag whether to restore zone }
  1154.     {$ENDC}
  1155.     
  1156.     BEGIN
  1157.     {$IFC FALSE}
  1158.     TestRecoverHandle := FALSE;
  1159.  
  1160.     { Test handle's Zone - if it comes from a different zone, then RecoverHandle won't work,
  1161.     in that case, set the current zone to be the handle's zone }
  1162.  
  1163.     itsZone := HandleZone(h);                            { get the handle's zone }
  1164.     IF MemError = noErr THEN
  1165.         BEGIN
  1166.         currentZone := GetZone;                         { get the current zone }
  1167.         IF itsZone = currentZone THEN                    { Are zones the same? }
  1168.             restoreZone := FALSE                        { …yes, so set flag to not restore }
  1169.         ELSE
  1170.             BEGIN
  1171.             restoreZone := TRUE;                        { …no, so set flag to restore zone }
  1172.             SetZone(itsZone);                            { and set the zone to be the handle's zone }
  1173.             END;
  1174.  
  1175.         TestRecoverHandle := RecoverHandle(masterPointer) = Handle(h);
  1176.  
  1177.         IF restoreZone THEN                             { restore the zone if the flag is set }
  1178.             SetZone(currentZone);
  1179.         END;
  1180.     {$ENDC}
  1181.     {    This function doesn't work correctly, so we set it to return true. The old code is left
  1182.         in for reference.    }
  1183.     TestRecoverHandle := TRUE;
  1184.     END;
  1185.  
  1186. {--------------------------------------------------------------------------------------------------}
  1187.  
  1188. {$S MAUtilitiesRes}
  1189.  
  1190. FUNCTION IsHandle(h: UNIV Handle): Boolean;
  1191. { Returns true if handle appears valid. }
  1192.  
  1193.     VAR
  1194.         masterPointer:        Ptr;
  1195.  
  1196.     BEGIN
  1197.     IsHandle := FALSE;
  1198.  
  1199.     IF
  1200.     { Test handle NILness }
  1201.       (h <> NIL)
  1202.     { Test handle Oddness }
  1203.       & NOT Odd(Ord(h)) THEN
  1204.         BEGIN
  1205.         masterPointer := Ptr(StripLong(h^));
  1206.         IsHandle :=
  1207.         { Test master pointer Oddness }
  1208.           (NOT Odd(Ord(masterPointer)))
  1209.         { Not Purged… does it recover? }
  1210.           & (((masterPointer <> NIL) & (TestRecoverHandle(masterPointer, h)))
  1211.         { Purged }
  1212.           | (masterPointer = NIL));
  1213.         END;
  1214.     END;
  1215.  
  1216. {--------------------------------------------------------------------------------------------------}
  1217.  
  1218. {$S MAUtilitiesRes}
  1219.  
  1220. FUNCTION IsHandleLocked(h: UNIV Handle): Boolean;
  1221. { Returns lockState of h. }
  1222.  
  1223.     CONST
  1224.         lockBit             = 7;
  1225.  
  1226.     VAR
  1227.         handleBits:         SignedByte;
  1228.  
  1229.     BEGIN
  1230.     handleBits := GetHandleBits(h);
  1231.     IF MemError <> noErr THEN                            { h might have been purged }
  1232.         IsHandleLocked := FALSE
  1233.     ELSE
  1234.         IsHandleLocked := BTST(handleBits, lockBit);
  1235.     END;
  1236.  
  1237. {--------------------------------------------------------------------------------------------------}
  1238.  
  1239. {$IFC qDebug}
  1240. {$S MAUtilitiesRes}
  1241.  
  1242. FUNCTION IsHandlePurged(h: UNIV Handle): Boolean;
  1243. { Returns purgeState of h. }
  1244.  
  1245.     BEGIN
  1246.     IF qDebug & NOT IsHandle(h) THEN
  1247.         BEGIN
  1248.         IF VerboseIsHandle(h) THEN;                     { Get the diagnosis printed }
  1249.         ProgramBreak('IsHandlePurged was not handed a handle, pretty handy, eh?');
  1250.         IsHandlePurged := TRUE;                         { !!! What is a decent result. shouldn't
  1251.                                                          developer just signal failure from the
  1252.                                                          debugger. We need to force the issue }
  1253.         END
  1254.     ELSE
  1255.         IsHandlePurged := h^ = NIL;
  1256.     END;
  1257. {$EndC}
  1258.  
  1259. {--------------------------------------------------------------------------------------------------}
  1260. {$S MAUtilitiesRes}
  1261.  
  1262. FUNCTION LengthRect(r: Rect;
  1263.                     vhs: VHSelect): INTEGER;
  1264.  
  1265.     BEGIN
  1266.     WITH r DO
  1267.         LengthRect := botRight.vh[vhs] - topLeft.vh[vhs];
  1268.     END;
  1269.  
  1270. {--------------------------------------------------------------------------------------------------}
  1271. {$S MAUtilitiesRes}
  1272.  
  1273. FUNCTION LongerSide(VAR r: Rect): VHSelect;
  1274.  
  1275.     BEGIN
  1276.     WITH r DO
  1277.         IF (bottom - top) >= (right - left) THEN
  1278.             LongerSide := v
  1279.         ELSE
  1280.             LongerSide := h;
  1281.     END;
  1282.  
  1283. {--------------------------------------------------------------------------------------------------}
  1284. {$S MADebug}
  1285.  
  1286. PROCEDURE LIntToHex(decNumber: UNIV longint;
  1287.                     VAR hexNumber: String8;
  1288.                     noOfDigits: INTEGER);
  1289.  
  1290.     VAR
  1291.         i:                    INTEGER;
  1292.  
  1293.     BEGIN
  1294.     noOfDigits := Min(noOfDigits, 8);
  1295.     hexNumber[0] := CHR(noOfDigits);
  1296.     FOR i := noOfDigits DOWNTO 1 DO
  1297.         BEGIN
  1298.         hexNumber[i] := kHexDigits[BAND(decNumber, 15) + 1];
  1299.         decNumber := BSR(decNumber, 4);
  1300.         END;
  1301.     END;
  1302.  
  1303. {--------------------------------------------------------------------------------------------------}
  1304. {$S MAUtilitiesRes}
  1305.  
  1306. FUNCTION LowerChar(ch: CHAR): CHAR;
  1307.  
  1308.     BEGIN
  1309.     IF (ch >= 'A') & (ch <= 'Z') THEN
  1310.         LowerChar := CHR(Ord(ch) + 32)
  1311.     ELSE
  1312.         LowerChar := ch;
  1313.     END;
  1314.  
  1315. {--------------------------------------------------------------------------------------------------}
  1316. {$S MAUtilitiesRes}
  1317.  
  1318. PROCEDURE LowerStr255(VAR s: Str255);
  1319.  
  1320.     VAR
  1321.         i:                    INTEGER;
  1322.  
  1323.     BEGIN
  1324.     FOR i := 1 TO LENGTH(s) DO
  1325.         IF (s[i] IN ['A'..'Z']) THEN
  1326.             s[i] := CHR(Ord(s[i]) + 32)
  1327.     END;
  1328.  
  1329. {--------------------------------------------------------------------------------------------------}
  1330. {$S MAUtilitiesRes}
  1331.  
  1332. FUNCTION MAUseResFile(refNum: INTEGER): INTEGER;
  1333. { UseResFile the newResFile and return the old CurResFile. }
  1334.  
  1335.     BEGIN
  1336.     MAUseResFile := CurResFile;
  1337.     UseResFile(refNum);
  1338.     END;
  1339.  
  1340. {--------------------------------------------------------------------------------------------------}
  1341. {$S MAUtilitiesRes}
  1342.  
  1343. FUNCTION MinMax(MinVal, expression, MaxVal: longint): longint;
  1344. {Returns the bounded minimum and maximum }
  1345.  
  1346.     BEGIN
  1347.     MinMax := Min(Max(expression, MinVal), MaxVal);
  1348.     END;
  1349.  
  1350. {--------------------------------------------------------------------------------------------------}
  1351. {$S MADebug}
  1352.  
  1353. PROCEDURE NumberToHex(theNumber: UNIV longint;
  1354.                       VAR hexString: Str255;
  1355.                       hexDigits: INTEGER);
  1356.  
  1357.     VAR
  1358.         tempString:         String8;
  1359.  
  1360.     BEGIN
  1361.     LIntToHex(theNumber, tempString, hexDigits);
  1362.     hexString := CONCAT('$', tempString);
  1363.     END;
  1364.  
  1365. {--------------------------------------------------------------------------------------------------}
  1366. {$S MADebug}
  1367.  
  1368. PROCEDURE PointerToHex(theNumber: UNIV longint;
  1369.                        VAR hexString: Str255;
  1370.                        hexDigits: INTEGER);
  1371.  
  1372.     VAR
  1373.         tempString:         String8;
  1374.  
  1375.     BEGIN
  1376.     IF theNumber = 0 THEN
  1377.         hexString := 'Nil'
  1378.     ELSE
  1379.         BEGIN
  1380.         LIntToHex(StripLong(theNumber), tempString, hexDigits);
  1381.         hexString := CONCAT('$', tempString);
  1382.         END;
  1383.     END;
  1384.  
  1385. {--------------------------------------------------------------------------------------------------}
  1386. {$S MAFile}
  1387.  
  1388. FUNCTION NumBlocks(numBytes: longint;
  1389.                    blkSize: longint): longint;
  1390.  
  1391.     BEGIN
  1392.     NumBlocks := (numBytes + blkSize - 1) DIV blkSize;
  1393.     END;
  1394.  
  1395. {--------------------------------------------------------------------------------------------------}
  1396. {$S MAFile}
  1397.  
  1398. FUNCTION MAOpenFile(name: Str255;
  1399.                     volRefnum: INTEGER;
  1400.                     openData, openRsrc: Boolean;
  1401.                     dataPerm, rsrcPerm: INTEGER;
  1402.                     VAR dataRefnum, rsrcRefnum: INTEGER): OSErr;
  1403.  
  1404.     VAR
  1405.         pb:                 HParamBlockRec;
  1406.         oldVRefnum:         INTEGER;
  1407.         result:             OSErr;
  1408.  
  1409.     PROCEDURE TestForError(err: OSErr);
  1410.  
  1411.         BEGIN
  1412.         IF err <> noErr THEN
  1413.             BEGIN
  1414.             MAOpenFile := err;
  1415.             exit(MAOpenFile);
  1416.             END;
  1417.         END;
  1418.  
  1419.     BEGIN
  1420.     {always open data fork, to establish that the file does exist}
  1421.     WITH pb DO
  1422.         BEGIN
  1423.         ioNamePtr := @name;
  1424.         ioVRefnum := volRefnum;
  1425.         ioVersNum := 0;
  1426.         ioPermssn := dataPerm;
  1427.         ioMisc := NIL;
  1428.         END;
  1429.     TestForError(FillInDirID(@pb));
  1430.  
  1431.     IF qNeedsROM128K | gConfiguration.hasHFS THEN
  1432.         result := PBHOpenDeny(@pb, FALSE)                { Try the shared volume open. }
  1433.     ELSE
  1434.         result := paramErr;
  1435.  
  1436.     IF result = paramErr THEN                            { Not on a shared volume, try HFS open. }
  1437.         BEGIN
  1438.         pb.ioPermssn := BAND(dataPerm, 3);
  1439.         result := PBHOpen(@pb, FALSE);
  1440.         END;
  1441.     TestForError(result);
  1442.  
  1443.     IF openData THEN
  1444.         dataRefnum := pb.ioRefnum
  1445.     ELSE
  1446.         BEGIN
  1447.         { we did not want the data fork open, so close it now }
  1448.         TestForError(FSClose(pb.ioRefnum));
  1449.         dataRefnum := kNoFileRefnum;
  1450.         END;
  1451.  
  1452.     IF openRsrc THEN
  1453.         BEGIN
  1454.         IF qNeedsROM128K | gConfiguration.hasROM128K THEN
  1455.             BEGIN
  1456.             rsrcRefnum := OpenRFPerm(name, volRefnum, BAND(rsrcPerm, 7));
  1457.             result := ResError;
  1458.             END
  1459.         ELSE
  1460.             BEGIN
  1461.             TestForError(GetVol(NIL, oldVRefnum));
  1462.             TestForError(SetVol(NIL, volRefnum));
  1463.  
  1464.             rsrcRefnum := OpenResFile(name);
  1465.  
  1466.             TestForError(SetVol(NIL, oldVRefnum));
  1467.             END;
  1468.  
  1469.         IF result <> noErr THEN
  1470.             rsrcRefnum := kNoFileRefnum;
  1471.  
  1472.         TestForError(result);
  1473.         END
  1474.     ELSE
  1475.         rsrcRefnum := kNoFileRefnum;
  1476.  
  1477.     MAOpenFile := noErr;
  1478.  
  1479.     END;
  1480.  
  1481. {--------------------------------------------------------------------------------------------------}
  1482. {$S MAUtilitiesRes}
  1483.  
  1484. VAR
  1485.     pSaveHText:         Handle;
  1486.     pMATextBoxHText:    Handle;
  1487.  
  1488. {$Push}
  1489. {$IFC qTrace} {$D+} {$ENDC}
  1490.  
  1491. PROCEDURE StdNoRect(verb: GrafVerb;
  1492.                     r: Rect);
  1493. { StdNoRect filters out the rect drawing calls. }
  1494.  
  1495.     BEGIN
  1496.     END;
  1497. {$Pop}
  1498.  
  1499. PROCEDURE MATextBox(text: Ptr;
  1500.                     itsLength: longint;
  1501.                     box: Rect;
  1502.                     itsJust: INTEGER;
  1503.                     autoWrap: Boolean;
  1504.                     wordBreak: ProcPtr;
  1505.                     eraseFirst: Boolean;
  1506.                     spaceForCaret: Boolean);
  1507.  
  1508.     CONST
  1509.         kTextBoxCaretSlopSize = 1;                        { Since TextBox uses TE to image the text,
  1510.                                                          we may need to adjust by 1 pixel. Reason:
  1511.                                                          TE draws beginning 1 pixel to the right to
  1512.                                                          allow for the insertion point (which we
  1513.                                                          won't have since this is drawn text, not
  1514.                                                          editable text).}
  1515.         kMaxTEChars         = 32000;                    { Actually TE suffers some other limitations
  1516.                                                          as well. Such as misbehaviour and or
  1517.                                                          bombing when the sum of the lineheights >
  1518.                                                          32k or a linewidth > 32k (overflows
  1519.                                                          QuickDraw space) But these are _MUCH_ more
  1520.                                                          difficult to test for in a quick way }
  1521.         kOurMaxHandleSize        = 256;                    { our Max handle size }
  1522.  
  1523.     VAR
  1524.         fInfo:                FontInfo;
  1525.         savedHText:         Handle;
  1526.         sysJust:            INTEGER;
  1527.         { these next two locals eat up lots of stack space...this could be improved by allocating
  1528.         a pointer for the one that is used (eg allocate a pointer for myCQDProcs if CDQ available) }
  1529.         myQDProcs:            QDProcs;
  1530.         myCQDProcs:         CQDProcs;
  1531.         hadQDProcs:            BOOLEAN;
  1532.         saveRectProc:        ProcPtr;
  1533.  
  1534.     PROCEDURE InitMyPrivateTE;
  1535.  
  1536.         CONST
  1537.             kZoneHeader         = 52;                    { 52 bytes for header }
  1538.             kZoneTrailer        = 12;                    { 12 bytes for trailer }
  1539.             kMPBlockHeader        = 8;                    { 8 bytes for Master Pointer block hdr }
  1540.             kInitialMstrPtrs    = 2;                    { 2 master pointers created initially }
  1541.             kSlop                = 32;                    { bytes of slop (just in case) }
  1542.             kZoneOverhead        = kZoneHeader + kZoneTrailer + kMPBlockHeader +
  1543.                                   4 * kInitialMstrPtrs + kSlop; { how large the zone overhead is }
  1544.  
  1545.         VAR
  1546.             aTEZonePtr:         Ptr;
  1547.             startPtr:            Ptr;
  1548.  
  1549.         BEGIN
  1550.         pMATextBoxHText := NIL;
  1551.  
  1552.         gMATextBoxTE := TENew(box, box);
  1553.         IF (gMATextBoxTE = NIL) THEN                    { can't allocate space for our terecord }
  1554.             exit(InitMyPrivateTE);
  1555.  
  1556.         { • save off several items of interest }
  1557.         WITH gMATextBoxTE^^ DO
  1558.             BEGIN
  1559.             gTEDefaultWordBreak := wordBreak;
  1560.             pSaveHText := hText;                        { save the text handle }
  1561.             END;
  1562.  
  1563.         { • Since TESetText (called near the end of MATextBox) hits the heap, we can speed this hit
  1564.         to the heap for small text lengths (<= 255), by allocating a special text handle in its own
  1565.         separate heap. We'll use this text handle whenever the text length is <= 255. }
  1566.  
  1567.         { • create a separate heap }
  1568.         aTEZonePtr := NewPtr(kOurMaxHandleSize + kZoneOverhead);
  1569.         IF (aTEZonePtr = NIL) THEN                        { can't allocate space for our heap }
  1570.             exit(InitMyPrivateTE);
  1571.         startPtr := Ptr(StripLong(aTEZonePtr));
  1572.         InitZone(NIL, kInitialMstrPtrs, Ptr(Ord(startPtr) + GetPtrSize(aTEZonePtr)), startPtr);
  1573.  
  1574.         { • InitZone sets the current zone to the newly created zone }
  1575.  
  1576.         { • allocate our new text handle in our new heap zone }
  1577.         pMATextBoxHText := NewHandle(kOurMaxHandleSize);     { the text handle }
  1578.  
  1579.         { • restore the heap zone  }
  1580.         SetZone(ApplicZone);
  1581.         END;
  1582.  
  1583.     FUNCTION IsColorPort(aGrafPtr: GrafPtr): BOOLEAN;
  1584.  
  1585.         BEGIN
  1586.         IsColorPort :=  (qNeedsColorQD | gConfiguration.hasColorQD)
  1587.         & (BAND(CGrafPtr(aGrafPtr)^.portVersion, $C000) = $0000C000)  { 2 hi bits. IM V pp. 49-50 }
  1588.         END;
  1589.  
  1590.  
  1591.     BEGIN
  1592.     { Create my goodies if necessary }
  1593.     IF gMATextBoxTE = NIL THEN
  1594.         BEGIN
  1595.         InitMyPrivateTE;
  1596.  
  1597.         IF gMATextBoxTE = NIL THEN                        { couldn't allocate the TE handle }
  1598.             BEGIN
  1599.             TextBox(text, itsLength, box, itsJust);     { default to TextBox in low memory }
  1600.             exit(MATextBox);
  1601.             END;
  1602.         END;
  1603.  
  1604.     { Setup the work TE with the necessary parameters }
  1605.     GetFontInfo(fInfo);                                 { Need to get font's height and ascent. }
  1606.  
  1607.     { Horse the intersection of the clip and the box into the TE's viewRect
  1608.     and then only draw at all if that rect is non empty }
  1609.     IF SectRect(thePort^.clipRgn^^.rgnBBox, box, gMATextBoxTE^^.viewRect) THEN
  1610.         BEGIN
  1611.         WITH gMATextBoxTE^^, fInfo DO
  1612.             BEGIN
  1613.             destRect := box;
  1614.             IF NOT spaceForCaret THEN                        { widen the destrect but not the visrect.
  1615.                                                              This lets the 1 pixel wide area to the
  1616.                                                              left of all text and the right of all text
  1617.                                                              go unshown. }
  1618.                 BEGIN
  1619.                 WITH destRect DO
  1620.                     BEGIN
  1621.                     left := left - kTextBoxCaretSlopSize;
  1622.                     right := right + kTextBoxCaretSlopSize;
  1623.                     END;
  1624.                 END;
  1625.  
  1626.  
  1627.             { Enforce minimum width on destRect ala IM-I pp. 383.  Although the text says that
  1628.             20 is a good number, using the widMax ensures that it is correct for all font sizes. }
  1629.             WITH destRect DO
  1630.                 right := left + Max(Max(right - left, widMax), 20);
  1631.     
  1632.             inPort := thePort;                                { Current port and its characteristics }
  1633.     
  1634.             txSize := thePort^.txSize;
  1635.             txFont := thePort^.txFont;
  1636.             txFace := thePort^.txFace;
  1637.             fontAscent := ascent;
  1638.             lineHeight := ascent + descent + leading;
  1639.             END;
  1640.  
  1641.         TESetJust(itsJust, gMATextBoxTE);                    { be good, use the trap }
  1642.     
  1643.         WITH gMATextBoxTE^^ DO
  1644.             BEGIN
  1645.             IF autoWrap THEN
  1646.                 crOnly := 0                                 {if >=0, word wrap}
  1647.             ELSE
  1648.                 crOnly := - 1;                                {if <0, new line at Return only}
  1649.     
  1650.             wordBreak := gTEDefaultWordBreak;
  1651.             END;
  1652.     
  1653.         IF wordBreak <> NIL THEN
  1654.             SetWordBreak(wordBreak, gMATextBoxTE);            { set the word break routine }
  1655.     
  1656.         IF (pMATextBoxHText <> NIL) THEN                    { if our private heap is set up }
  1657.             BEGIN
  1658.             IF itsLength <= kOurMaxHandleSize  THEN        { short strings go in the mini-heap }
  1659.                 gMATextBoxTE^^.hText := pMATextBoxHText
  1660.             ELSE
  1661.                 gMATextBoxTE^^.hText := pSaveHText;
  1662.             END;
  1663.     
  1664.         TESetText(text, Min(itsLength, kMaxTEChars), gMATextBoxTE);
  1665.     
  1666.         { if called with eraseFirst TRUE, then let TEUpdate image with its built-in EraseRect }
  1667.         IF eraseFirst THEN
  1668.             BEGIN
  1669.             EraseRect(gMATextBoxTE^^.viewRect); { Oh yeah?  Some versions of TE _DON'T_ erase first! }
  1670.             TEUpdate(box, gMATextBoxTE);
  1671.             END
  1672.         ELSE
  1673.             BEGIN
  1674.             { replace the existing QD procs ( standard or externally supplied )
  1675.             so that the (<potential>, see comment above) EraseRect in TEUpdate is ignored }
  1676.     
  1677.             IF thePort^.grafProcs <> NIL THEN
  1678.                 BEGIN
  1679.                 hadQDProcs := TRUE;
  1680.                 saveRectProc := thePort^.grafProcs^.rectProc;
  1681.                 thePort^.grafProcs^.rectProc := @StdNoRect;
  1682.                 END
  1683.             ELSE
  1684.                 BEGIN
  1685.                 hadQDProcs := FALSE;
  1686.                 IF IsColorPort(thePort) THEN
  1687.                     BEGIN
  1688.                     SetStdCProcs(myCQDProcs);
  1689.                     myCQDProcs.rectProc := @StdNoRect;
  1690.                     thePort^.grafProcs := @myCQDProcs;
  1691.                     END
  1692.                 ELSE
  1693.                     BEGIN
  1694.                     SetStdProcs(myQDProcs);
  1695.                     myQDProcs.rectProc := @StdNoRect;
  1696.                     thePort^.grafProcs := @myQDProcs;
  1697.                     END;
  1698.                 END;
  1699.     
  1700.             { Now do the imaging }
  1701.             TEUpdate(box, gMATextBoxTE);
  1702.     
  1703.             { Restore the QDProcs or eliminate the QDProcs, take yer pick. }
  1704.             IF hadQDProcs THEN
  1705.                 thePort^.grafProcs^.rectProc := saveRectProc
  1706.             ELSE
  1707.                 thePort^.grafProcs := NIL;
  1708.                 
  1709.             END;
  1710.         END;
  1711.     END;
  1712.  
  1713. {--------------------------------------------------------------------------------------------------}
  1714. {$S MAUtilitiesRes}
  1715.  
  1716. PROCEDURE MADrawString(s: StringPtr;
  1717.                        box: Rect;
  1718.                        justification: INTEGER);
  1719.  
  1720.     VAR
  1721.         theFontInfo:        FontInfo;
  1722.         widthOfString:        INTEGER;
  1723.         boxWidth:            INTEGER;
  1724.  
  1725.     BEGIN
  1726.     GetFontInfo(theFontInfo);
  1727.     widthOfString := StringWidth(s^);
  1728.     WITH box DO
  1729.         BEGIN
  1730.         boxWidth := right - left;
  1731.         IF widthOfString < boxWidth THEN
  1732.             BEGIN
  1733.             CASE GetActualJustification(justification) OF
  1734.                 teJustLeft: ;
  1735.                 teJustCenter:
  1736.                     left := left + (boxWidth - widthOfString) DIV 2;
  1737.                 teJustRight:
  1738.                     left := left + boxWidth - widthOfString;
  1739.                 teForceLeft: ;
  1740.             END;
  1741.             END;
  1742.  
  1743.         MoveTo(left, top + theFontInfo.ascent);
  1744.         DrawString(s^);
  1745.         END;
  1746.     END;
  1747.  
  1748. {--------------------------------------------------------------------------------------------------}
  1749. {$S MAUtilitiesRes}
  1750.  
  1751. FUNCTION PinOnRect(theRect: Rect;
  1752.                    thePt: Point): longint;
  1753.  
  1754.     BEGIN
  1755.     IF thePt.h < theRect.left THEN
  1756.         thePt.h := theRect.left;
  1757.     IF thePt.h > theRect.right THEN
  1758.         thePt.h := theRect.right;
  1759.     IF thePt.v < theRect.top THEN
  1760.         thePt.v := theRect.top;
  1761.     IF thePt.v > theRect.bottom THEN
  1762.         thePt.v := theRect.bottom;
  1763.  
  1764.     PinOnRect := longint(thePt);
  1765.     END;
  1766.  
  1767. {--------------------------------------------------------------------------------------------------}
  1768. {$S WWSeg}
  1769.  
  1770. FUNCTION ReadInteger(prompt: Str255): INTEGER;
  1771.  
  1772.     VAR
  1773.         i:                    INTEGER;
  1774.  
  1775.     BEGIN
  1776.     {$IFC qDebug}
  1777.     DebugForceOutput(forceOn, forceUnchanged);
  1778.     {$EndC}
  1779.     Write(prompt);
  1780.     Readln(i);
  1781.     {$IFC qDebug}
  1782.     DebugEndForce;
  1783.     {$EndC}
  1784.     ReadInteger := i;
  1785.     END;
  1786.  
  1787. {--------------------------------------------------------------------------------------------------}
  1788. {$S WWSeg}
  1789.  
  1790. FUNCTION ReadYesNo(prompt: Str255): Boolean;
  1791.  
  1792.     VAR
  1793.         s:                    Str255;
  1794.  
  1795.     BEGIN
  1796.     {$IFC qDebug}
  1797.     DebugForceOutput(forceOn, forceUnchanged);
  1798.     {$EndC}
  1799.     Write(prompt);
  1800.     Readln(s);
  1801.     {$IFC qDebug}
  1802.     DebugEndForce;
  1803.     {$EndC}
  1804.     ReadYesNo := (s <> '') & (s[1] IN ['y', 'Y']);
  1805.     END;
  1806.  
  1807. {--------------------------------------------------------------------------------------------------}
  1808. {$S MAUtilitiesRes}
  1809.  
  1810. FUNCTION RectsNest(outer, inner: Rect): Boolean;
  1811.  
  1812.     BEGIN
  1813.     WITH inner DO
  1814.         RectsNest := (left >= outer.left) & (right <= outer.right) & (top >= outer.top) & (bottom <=
  1815.                      outer.bottom);
  1816.     END;
  1817.  
  1818. {--------------------------------------------------------------------------------------------------}
  1819. {$S MAUtilitiesRes}
  1820.  
  1821. FUNCTION VRectsNest(outer, inner: VRect): Boolean;
  1822.  
  1823.     BEGIN
  1824.     WITH inner DO
  1825.         VRectsNest := (left >= outer.left) & (right <= outer.right) & (top >= outer.top) &
  1826.                       (bottom <= outer.bottom);
  1827.     END;
  1828.  
  1829. {--------------------------------------------------------------------------------------------------}
  1830. {$S MAUtilitiesRes}
  1831.  
  1832. FUNCTION RoundUp(aNumber: longint;
  1833.                  aModulus: INTEGER): longint;
  1834.  
  1835.     BEGIN
  1836.     RoundUp := ((aNumber + aModulus - 1) DIV aModulus) * aModulus;
  1837.     END;
  1838.  
  1839. {--------------------------------------------------------------------------------------------------}
  1840. {$S MAUtilitiesRes}
  1841.  
  1842. PROCEDURE ScrapStuffFields(aTitle: Str255;
  1843.                            VAR aScrapStuff: ScrapStuff;
  1844.                            PROCEDURE DoToField(fieldName: Str255;
  1845.                                                fieldAddr: Ptr;
  1846.                                                fieldType: INTEGER));
  1847.  
  1848.     BEGIN
  1849.     DoToField(aTitle, NIL, bTitle);
  1850.     DoToField('  scrapSize', @aScrapStuff.scrapSize, bLongint);
  1851.     DoToField('  scrapHandle', @aScrapStuff.scrapHandle, bHandle);
  1852.     DoToField('  scrapCount', @aScrapStuff.scrapCount, bInteger);
  1853.     DoToField('  scrapState', @aScrapStuff.scrapState, bInteger);
  1854.     IF aScrapStuff.scrapName <> NIL THEN
  1855.         DoToField('  scrapName', @aScrapStuff.scrapName^, bString)
  1856.     ELSE
  1857.         DoToField('  scrapName', NIL, bPointer);
  1858.     END;
  1859.  
  1860. {--------------------------------------------------------------------------------------------------}
  1861. {$S MAUtilitiesRes}
  1862.  
  1863. FUNCTION SetKeyScript(newKeyScript: INTEGER): INTEGER;
  1864.  
  1865.     VAR
  1866.         currentKeyScript:    INTEGER;
  1867.  
  1868.     BEGIN
  1869.     IF qNeedsScriptManager | gConfiguration.hasScriptManager THEN
  1870.         BEGIN
  1871.         currentKeyScript := GetEnvirons(smKeyScript);
  1872.         IF currentKeyScript <> newKeyScript THEN
  1873.             KeyScript(newKeyScript);
  1874.         SetKeyScript := currentKeyScript;
  1875.         END
  1876.     ELSE
  1877.         BEGIN
  1878.         { ??? what it the correct thing to do if we get here? }
  1879.         END;
  1880.     END;
  1881.  
  1882. {--------------------------------------------------------------------------------------------------}
  1883. {$S MAUtilitiesRes}                                     {Must be in Main segment and cannot call to
  1884.                                                          any other segment.}
  1885.  
  1886. PROCEDURE SetHandleBits(h: Handle;
  1887.                         theBits: SignedByte);
  1888.  
  1889.     BEGIN
  1890.     IF qNeedsROM128K | gConfiguration.hasROM128K THEN
  1891.         HSetState(h, theBits)
  1892.     ELSE
  1893.         SignedBytePtr(h)^ := theBits;
  1894.     END;
  1895.  
  1896. {--------------------------------------------------------------------------------------------------}
  1897. {$IFC NOT qNeedsColorQD}                                { Becomes an inline if we know the machine
  1898.                                                          has color QD }
  1899. {$S MAUtilitiesRes}
  1900.  
  1901. PROCEDURE SetIfBkColor(aColor: RGBColor);
  1902.  
  1903.     CONST
  1904.         SignBit             = 15;
  1905.  
  1906.     VAR
  1907.         index:                INTEGER;
  1908.         oldColor:            longint;
  1909.  
  1910.     BEGIN
  1911.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1912.         BEGIN
  1913.         { if not color port or color doesn't match then make trap }
  1914.         WITH CGrafPtr(thePort)^ DO
  1915.             IF (BAND(portVersion, $C000) <> $0000C000) | NOT EqualBlocks(@rgbBkColor, @aColor,
  1916.                                                                          sizeof(RGBColor)) THEN
  1917.                 RGBBackColor(aColor);
  1918.         END
  1919.     ELSE
  1920.         BEGIN
  1921.         index := 0;                                     { Prime index }
  1922.         IF BTST(aColor.red, SignBit) THEN                { Set bit if red >= $8000 }
  1923.             index := 4;
  1924.         IF BTST(aColor.green, SignBit) THEN             { Set bit if green >= $8000 }
  1925.             index := index + 2;
  1926.         IF BTST(aColor.blue, SignBit) THEN                { Set bit if blue >= $8000 }
  1927.             index := index + 1;
  1928.         CASE index OF
  1929.             0:
  1930.                 oldColor := blackColor;
  1931.             1:
  1932.                 oldColor := blueColor;
  1933.             2:
  1934.                 oldColor := greenColor;
  1935.             3:
  1936.                 oldColor := cyanColor;
  1937.             4:
  1938.                 oldColor := redColor;
  1939.             5:
  1940.                 oldColor := magentaColor;
  1941.             6:
  1942.                 oldColor := yellowColor;
  1943.             7:
  1944.                 oldColor := whiteColor;
  1945.         END;
  1946.         BackColor(oldColor);
  1947.         END;
  1948.     END;
  1949. {$ENDC}
  1950.  
  1951. {--------------------------------------------------------------------------------------------------}
  1952. {$IFC NOT qNeedsColorQD}                                { Becomes an inline if we know the machine
  1953.                                                          has color QD }
  1954. {$S MAUtilitiesRes}
  1955.  
  1956. PROCEDURE SetIfColor(aColor: RGBColor);
  1957.  
  1958.     CONST
  1959.         SignBit             = 15;
  1960.  
  1961.     VAR
  1962.         index:                INTEGER;
  1963.         oldColor:            longint;
  1964.  
  1965.     BEGIN
  1966.     IF qNeedsColorQD | gConfiguration.hasColorQD THEN
  1967.         BEGIN
  1968.         { if not color port or color doesn't match then make trap }
  1969.         WITH CGrafPtr(thePort)^ DO
  1970.             IF (BAND(portVersion, $C000) <> $0000C000) | NOT EqualBlocks(@rgbFgColor, @aColor,
  1971.                                                                          sizeof(RGBColor)) THEN
  1972.                 RGBForeColor(aColor);
  1973.         END
  1974.     ELSE
  1975.         BEGIN
  1976.         index := 0;                                     { Prime index }
  1977.         IF BTST(aColor.red, SignBit) THEN                { Set bit if red >= $8000 }
  1978.             index := 4;
  1979.         IF BTST(aColor.green, SignBit) THEN             { Set bit if green >= $8000 }
  1980.             index := index + 2;
  1981.         IF BTST(aColor.blue, SignBit) THEN                { Set bit if blue >= $8000 }
  1982.             index := index + 1;
  1983.         CASE index OF
  1984.             0:
  1985.                 oldColor := blackColor;
  1986.             1:
  1987.                 oldColor := blueColor;
  1988.             2:
  1989.                 oldColor := greenColor;
  1990.             3:
  1991.                 oldColor := cyanColor;
  1992.             4:
  1993.                 oldColor := redColor;
  1994.             5:
  1995.                 oldColor := magentaColor;
  1996.             6:
  1997.                 oldColor := yellowColor;
  1998.             7:
  1999.                 oldColor := whiteColor;
  2000.         END;
  2001.         ForeColor(oldColor);
  2002.         END;
  2003.     END;
  2004. {$ENDC}
  2005.  
  2006. {--------------------------------------------------------------------------------------------------}
  2007. {$S MAUtilitiesRes}
  2008.  
  2009. PROCEDURE GetPortTextStyle(VAR theTextStyle: TextStyle);
  2010.  
  2011.     BEGIN
  2012.     WITH thePort^, theTextStyle DO
  2013.         BEGIN
  2014.         tsFont := txFont;
  2015.         tsFace := txFace;
  2016.         tsSize := txSize;
  2017.         GetIfColor(tsColor);
  2018.         END;
  2019.     END;
  2020.  
  2021. {--------------------------------------------------------------------------------------------------}
  2022. {$S MAUtilitiesRes}
  2023.  
  2024. PROCEDURE SetPortTextStyle(theTextStyle: TextStyle);
  2025.  
  2026.     BEGIN
  2027.     { Don't make the traps unless we need to }
  2028.     WITH thePort^, theTextStyle DO
  2029.         BEGIN
  2030.         IF txFont <> tsFont THEN
  2031.             TextFont(tsFont);
  2032.         IF txFace <> tsFace THEN
  2033.             TextFace(tsFace);
  2034.         IF txSize <> tsSize THEN
  2035.             TextSize(tsSize);
  2036.         SetIfColor(tsColor);
  2037.         END;
  2038.     END;
  2039.  
  2040. {--------------------------------------------------------------------------------------------------}
  2041. {$Push}                                                 { Must be in Main segment, and generic code,
  2042.                                                          because InitToolBox calls this }
  2043. {$MC68020-}
  2044. {$S MAUtilitiesRes}
  2045.  
  2046. PROCEDURE SetRGBColor(VAR RGB: RGBColor;
  2047.                       red, green, blue: INTEGER);
  2048.  
  2049.     BEGIN
  2050.     RGB.red := red;
  2051.     RGB.green := green;
  2052.     RGB.blue := blue;
  2053.     END;
  2054. {$Pop}
  2055.  
  2056. {--------------------------------------------------------------------------------------------------}
  2057. {$S MAUtilitiesRes}
  2058.  
  2059. PROCEDURE SetTextStyle(VAR theTextStyle: TextStyle;
  2060.                        theFont: INTEGER;
  2061.                        theStyle: Style;
  2062.                        theSize: INTEGER;
  2063.                        theColor: RGBColor);
  2064.  
  2065.     BEGIN
  2066.     WITH theTextStyle DO
  2067.         BEGIN
  2068.         tsFont := theFont;
  2069.         tsFace := theStyle;
  2070.         tsSize := theSize;
  2071.         tsColor := theColor;
  2072.         END;
  2073.     END;
  2074.  
  2075. {--------------------------------------------------------------------------------------------------}
  2076. {$S MADebug}
  2077.  
  2078. PROCEDURE StdFieldToString(theData: Ptr;
  2079.                            fieldType: INTEGER;
  2080.                            VAR theString: Str255);
  2081.  
  2082.     CONST
  2083.         adnFrame            = [adnLineTop, adnLineLeft, adnLineBottom, adnLineRight];
  2084.         kDecPrec            = 4;                        { Change this if you want more decimal
  2085.                                                          precision in extended}
  2086.  
  2087.     TYPE
  2088.         TAlias                = RECORD
  2089.             CASE INTEGER OF
  2090.                 bBoolean:
  2091.                     (asBoolean:          Boolean);
  2092.                 bFontName, bCmdNumber, bHighByte, bLowByte, bHexInteger, bInteger:
  2093.                     (asInteger:          INTEGER);
  2094.                 bFixed, bHexLongInt, bLongint:
  2095.                     (asLongInt:          longint);
  2096.                 bString:
  2097.                     (asString:             Str255);
  2098.                 bChar:
  2099.                     (asChar:             CHAR);
  2100.                 bGrafPtr, bWindowPtr, bPointer:
  2101.                     (asPointer:          Ptr);
  2102.                 bRgnHandle, bControlHandle, bTEHandle, bHandle:
  2103.                     (asHandle:             Handle);
  2104.                 bPoint:
  2105.                     (asPoint:             Point);
  2106.                 bRect:
  2107.                     (asRect:             Rect);
  2108.                 bObject:
  2109.                     (asObject:             Handle);
  2110.                 bByte:
  2111.                     (asByte:             SignedByte);
  2112.                 bHLState:
  2113.                     (asHLState:          SignedByte);
  2114.                 bIdType, bResType, bOSType:
  2115.                     (asOSType:             OSType);
  2116.                 bPattern:
  2117.                     (asPattern:          Pattern);
  2118.                 bRGBColor:
  2119.                     (asRGBColor:         RGBColor);
  2120.                 bStyle:
  2121.                     (asStyle:             Style);
  2122.                 bVCoordinate:
  2123.                     (asVCoordinate:      VCoordinate);
  2124.                 bVPoint:
  2125.                     (asVPoint:             VPoint);
  2126.                 bVRect:
  2127.                     (asVRect:             VRect);
  2128.                 bStringHandle:
  2129.                     (asStrHandle:         StringHandle);
  2130.                 bCntlAdornment:
  2131.                     (asCntlAdornment:     CntlAdornment);
  2132.                 bSizeDeterminer:
  2133.                     (asSizeDeterminer:     SignedByte);
  2134.                 bReal, bSingle:
  2135.                     (asReal:             Real);
  2136.                 bDouble:
  2137.                     (asDouble:             Double);
  2138.                 bExtended:
  2139.                     (asExtended:         Extended);
  2140.                 bVHSelect:
  2141.                     (asVHSelect:         VHSelect);
  2142.             END;
  2143.  
  2144.     VAR
  2145.         alias:                ^TAlias;
  2146.         aString:            Str255;
  2147.         hexString:            String8;
  2148.         i:                    INTEGER;
  2149.         { Extended support }
  2150.         aDecForm:            DecForm;
  2151.         x:                    Extended;
  2152.         NumStr:             DecStr;
  2153.  
  2154.     PROCEDURE CheckStyleItem(s: StyleItem;
  2155.                              name: Str255);
  2156.  
  2157.         BEGIN
  2158.         IF s IN alias^.asStyle THEN
  2159.             IF theString = '[' THEN
  2160.                 theString := CONCAT(theString, name)
  2161.             ELSE
  2162.                 theString := CONCAT(theString, ',', name);
  2163.         END;
  2164.  
  2165.     PROCEDURE CheckAdornment(p: CntlAdornment;
  2166.                              name: Str255);
  2167.  
  2168.         BEGIN
  2169.         { "set1 <= set2" means set1 is wholly contained in set2 }
  2170.         IF p <= alias^.asCntlAdornment THEN
  2171.             IF theString = '[' THEN
  2172.                 theString := CONCAT(theString, name)
  2173.             ELSE
  2174.                 theString := CONCAT(theString, ',', name);
  2175.         END;
  2176.  
  2177.     BEGIN
  2178.     alias := Pointer(theData);
  2179.     theString := '';
  2180.     WITH alias^ DO
  2181.         CASE fieldType OF
  2182.             bBoolean:
  2183.                 BEGIN
  2184.                 NumberToHex(asByte, theString, 2);
  2185.                 Insert(' (', theString, 1);
  2186.                 theString := CONCAT(theString, ')');
  2187.                 Insert(gBoolString[Ord(asBoolean) <> 0], theString, 1);
  2188.                 END;
  2189.             bFontName:
  2190.                 GetFontName(asInteger, theString);
  2191.             bInteger:
  2192.                 NumToString(asInteger, theString);
  2193.             bLongint:
  2194.                 NumToString(asLongInt, theString);
  2195.             bHexInteger:
  2196.                 NumberToHex(asInteger, theString, 4);
  2197.             bHexLongInt:
  2198.                 NumberToHex(asLongInt, theString, 8);
  2199.             bHighByte:
  2200.                 NumberToHex(BSR(BAND(asInteger, $FF00), 8), theString, 2);
  2201.             bLowByte:
  2202.                 NumberToHex(BAND(asInteger, $00FF), theString, 2);
  2203.             bFixed:
  2204.                 BEGIN
  2205.                 NumToString(HiWrd(asLongInt), aString);
  2206.                 NumToString(LoWrd(asLongInt), theString);
  2207.                 theString := CONCAT(aString, ':', theString);
  2208.                 END;
  2209.             bString:
  2210.                 theString := asString;
  2211.             bChar:
  2212.                 BEGIN
  2213.                 theString := ' ';
  2214.                 theString[1] := asChar;
  2215.                 END;
  2216.             bGrafPtr, bWindowPtr, bPointer:
  2217.                 BEGIN
  2218.                 PointerToHex(ORD4(asPointer), aString, 8);
  2219.                 IF Odd(ORD4(asPointer)) THEN
  2220.                     theString := CONCAT('INVALID! (', aString, ')')
  2221.                 ELSE IF asHandle = NIL THEN
  2222.                     theString := 'Nil'
  2223.                 ELSE
  2224.                     theString := aString;
  2225.                 END;
  2226.             bRgnHandle, bControlHandle, bTEHandle, bHandle:
  2227.                 BEGIN
  2228.                 PointerToHex(ORD4(asHandle), aString, 8);
  2229.                 IF Odd(ORD4(asHandle)) THEN
  2230.                     theString := CONCAT('INVALID! (', aString, ')')
  2231.                 ELSE IF asHandle = NIL THEN
  2232.                     theString := 'Nil'
  2233.                 ELSE
  2234.                     theString := aString;
  2235.                 END;
  2236.             bPoint:
  2237.                 BEGIN
  2238.                 NumToString(asPoint.h, aString);
  2239.                 NumToString(asPoint.v, theString);
  2240.                 theString := CONCAT('(h:', aString, ', v:', theString, ')');
  2241.                 END;
  2242.             bRect:
  2243.                 BEGIN
  2244.                 NumToString(asRect.left, aString);
  2245.                 NumToString(asRect.top, theString);
  2246.                 theString := CONCAT('(l:', aString, ', t:', theString, ')/(r:');
  2247.                 NumToString(asRect.right, aString);
  2248.                 theString := CONCAT(theString, aString, ', b:');
  2249.                 NumToString(asRect.bottom, aString);
  2250.                 theString := CONCAT(theString, aString, ')');
  2251.                 END;
  2252.             bObject:
  2253.                 BEGIN
  2254.                 PointerToHex(ORD4(asObject), aString, 8);
  2255.                 IF Odd(ORD4(asObject)) THEN
  2256.                     theString := CONCAT('INVALID! (', aString, ')')
  2257.                 ELSE IF asObject = NIL THEN
  2258.                     theString := 'Nil'
  2259.                 ELSE
  2260.                     theString := aString;
  2261.                 END;
  2262.             bByte:
  2263.                 NumToString(asByte, theString);
  2264.             bHLState:
  2265.                 CASE asHLState OF
  2266.                     1:
  2267.                         theString := 'hlOff';
  2268.                     2:
  2269.                         theString := 'hlDim';
  2270.                     4:
  2271.                         theString := 'hlOn';
  2272.                     OTHERWISE
  2273.                         BEGIN
  2274.                         NumToString(asHLState, aString);
  2275.                         theString := CONCAT('INVALID! (', aString, ')');
  2276.                         END;
  2277.                 END;
  2278.             bCmdNumber:
  2279.                 NumToString(asInteger, theString);
  2280.             bIdType, bResType, bOSType:
  2281.                 BEGIN
  2282.                 theString := '''    ''';
  2283.                 FOR i := 1 TO 4 DO
  2284.                     theString[i + 1] := asOSType[i];
  2285.                 END;
  2286.             bPattern:
  2287.                 BEGIN
  2288.                 theString := '$';
  2289.                 FOR i := 0 TO 7 DO
  2290.                     BEGIN
  2291.                     LIntToHex(asPattern[i], hexString, 2);
  2292.                     theString := CONCAT(theString, hexString);
  2293.                     END;
  2294.                 END;
  2295.             bRGBColor:
  2296.                 WITH asRGBColor DO
  2297.                     IF (red = 0) & (green = 0) & (blue = 0) THEN
  2298.                         theString := 'Black'
  2299.                     ELSE IF (red = $FFFF) & (green = $FFFF) & (blue = $FFFF) THEN
  2300.                         theString := 'White'
  2301.                     ELSE
  2302.                         BEGIN
  2303.                         NumberToHex(asRGBColor.red, theString, 4);
  2304.                         NumberToHex(asRGBColor.green, aString, 4);
  2305.                         theString := CONCAT(theString, '/', aString);
  2306.                         NumberToHex(asRGBColor.blue, aString, 4);
  2307.                         theString := CONCAT(theString, '/', aString);
  2308.                         END;
  2309.             bStyle:
  2310.                 BEGIN
  2311.                 theString := '[';
  2312.                 CheckStyleItem(bold, 'bold');
  2313.                 CheckStyleItem(italic, 'italic');
  2314.                 CheckStyleItem(underline, 'underline');
  2315.                 CheckStyleItem(outline, 'outline');
  2316.                 CheckStyleItem(shadow, 'shadow');
  2317.                 CheckStyleItem(condense, 'condense');
  2318.                 CheckStyleItem(extend, 'extend');
  2319.                 theString := CONCAT(theString, ']');
  2320.                 END;
  2321.             bVCoordinate:
  2322.                 NumToString(asVCoordinate, theString);
  2323.             bVPoint:
  2324.                 BEGIN
  2325.                 NumToString(asVPoint.h, aString);
  2326.                 NumToString(asVPoint.v, theString);
  2327.                 theString := CONCAT('(h:', aString, ', v:', theString, ')');
  2328.                 END;
  2329.             bVRect:
  2330.                 BEGIN
  2331.                 NumToString(asVRect.left, aString);
  2332.                 NumToString(asVRect.top, theString);
  2333.                 theString := CONCAT('(l:', aString, ', t:', theString, ')/(r:');
  2334.                 NumToString(asVRect.right, aString);
  2335.                 theString := CONCAT(theString, aString, ', b:');
  2336.                 NumToString(asVRect.bottom, aString);
  2337.                 theString := CONCAT(theString, aString, ')');
  2338.                 END;
  2339.             bStringHandle:
  2340.                 IF asStrHandle = NIL THEN
  2341.                     theString := 'Nil'
  2342.                 ELSE
  2343.                     theString := asStrHandle^^;
  2344.             bCntlAdornment:
  2345.                 BEGIN
  2346.                 theString := '[';
  2347.                 IF adnFrame <= asCntlAdornment THEN
  2348.                     CheckAdornment(adnFrame, 'frame')
  2349.                 ELSE
  2350.                     BEGIN
  2351.                     CheckAdornment([adnLineTop], 'top');
  2352.                     CheckAdornment([adnLineLeft], 'left');
  2353.                     CheckAdornment([adnLineBottom], 'bottom');
  2354.                     CheckAdornment([adnLineRight], 'right');
  2355.                     END;
  2356.                 { CheckAdornment(adnPatFill, 'fill'); }
  2357.                 CheckAdornment([adnOval], 'oval');
  2358.                 CheckAdornment([adnRRect], 'rrect');
  2359.                 CheckAdornment([adnShadow], 'shadow');
  2360.                 theString := CONCAT(theString, ']');
  2361.                 END;
  2362.             bSizeDeterminer:
  2363.                 CASE asSizeDeterminer OF
  2364.                     0:
  2365.                         theString := 'sizeSuperView';
  2366.                     1:
  2367.                         theString := 'sizeRelSuperView';
  2368.                     2:
  2369.                         theString := 'sizePage';
  2370.                     3:
  2371.                         theString := 'sizeFillPages';
  2372.                     4:
  2373.                         theString := 'sizeVariable';
  2374.                     5:
  2375.                         theString := 'sizeFixed';
  2376.                 END;
  2377.             bReal, bSingle:
  2378.                 BEGIN
  2379.                 aDecForm.Style := FixedDecimal;
  2380.                 aDecForm.digits := kDecPrec;
  2381.                 x := asReal;
  2382.                 Num2Str(aDecForm, x, NumStr);
  2383.                 theString := Str255(NumStr);
  2384.                 END;
  2385.             bDouble:
  2386.                 BEGIN
  2387.                 aDecForm.Style := FixedDecimal;
  2388.                 aDecForm.digits := kDecPrec;
  2389.                 x := asDouble;
  2390.                 Num2Str(aDecForm, x, NumStr);
  2391.                 theString := Str255(NumStr);
  2392.                 END;
  2393.             bExtended:
  2394.                 BEGIN
  2395.                 aDecForm.Style := FixedDecimal;
  2396.                 aDecForm.digits := kDecPrec;
  2397.                 x := asExtended;
  2398.                 Num2Str(aDecForm, x, NumStr);
  2399.                 theString := Str255(NumStr);
  2400.                 END;
  2401.             bVHSelect:
  2402.                 BEGIN
  2403.                 CASE asVHSelect OF
  2404.                     v:
  2405.                         theString := 'v';
  2406.                     h:
  2407.                         theString := 'h';
  2408.                     OTHERWISE
  2409.                         BEGIN
  2410.                         NumToString(ORD(asVHSelect), aString);
  2411.                         theString := CONCAT('INVALID! (', aString, ')');
  2412.                         END;
  2413.                 END;
  2414.                 END;
  2415.         END;
  2416.     END;
  2417.  
  2418. {--------------------------------------------------------------------------------------------------}
  2419.  
  2420. FUNCTION StripLong(address: UNIV Ptr): longint;
  2421.     EXTERNAL;
  2422.  
  2423. {--------------------------------------------------------------------------------------------------}
  2424. {$S MAFields}
  2425.  
  2426. PROCEDURE TextStyleFields(aTitle: Str255;
  2427.                           VAR aStyle: TextStyle;
  2428.                           PROCEDURE DoToField(fieldName: Str255;
  2429.                                               fieldAddr: Ptr;
  2430.                                               fieldType: INTEGER));
  2431.  
  2432.     BEGIN
  2433.     DoToField(aTitle, NIL, bTitle);
  2434.     DoToField('  Font', @aStyle.tsFont, bFontName);
  2435.     DoToField('  Face', @aStyle.tsFace, bStyle);
  2436.     DoToField('  Size', @aStyle.tsSize, bInteger);
  2437.     DoToField('  Color', @aStyle.tsColor, bRGBColor);
  2438.     END;
  2439.  
  2440. {--------------------------------------------------------------------------------------------------}
  2441. {$Push}
  2442. {$MC68020-}
  2443. {$S MAUtilitiesRes}
  2444.  
  2445. FUNCTION NumToolboxTraps: INTEGER;
  2446. { InitGraf is always implemented (trap $A86E). If the trap table is big enough, trap $AA6E
  2447. will always point to either Unimplemented or some other trap, but will never be the same
  2448. as InitGraf. Thus, you can check the size of the trap table by asking if the address of
  2449. trap $A86E is the same as $AA6E. }
  2450.  
  2451.     BEGIN
  2452.     IF NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) THEN
  2453.         NumToolboxTraps := $200
  2454.     ELSE
  2455.         NumToolboxTraps := $400;
  2456.     END;
  2457. {$Pop}
  2458.  
  2459. {--------------------------------------------------------------------------------------------------}
  2460. {$Push}
  2461. {$MC68020-}
  2462. {$S MAUtilitiesRes}
  2463.  
  2464. FUNCTION TrapExists(theTrap: INTEGER): Boolean;
  2465. { Thank-you François Grieu! }
  2466.  
  2467.     CONST
  2468.         UnimNb = _Unimplemented-$A800; {Trap NUMBER of an unimplemented Tool trap}
  2469.  
  2470.     VAR
  2471.         theTrapType: TrapType;
  2472.  
  2473.     BEGIN
  2474.  
  2475.     { this is a safety check, for debug mode }
  2476.     IF qDebug THEN
  2477.         IF BAND(theTrap,$0A000)<>$0A000 THEN
  2478.             BEGIN
  2479.             WrLblHexInt('TrapExists wants trap WORDs, not ', theTrap);
  2480.             WriteLn;
  2481.             ProgramBreak('');
  2482.             END;
  2483.  
  2484.     { here theTrap is a trap WORD }
  2485.     theTrapType := GetTrapType(theTrap); { decide from bit 11 if trap is a Tool or OS trap }
  2486.     IF (theTrapType = OsTrap) THEN
  2487.         theTrap :=  BAND(theTrap, $00FF)
  2488.     ELSE
  2489.         BEGIN
  2490.         theTrap := BAND(theTrap, $03FF);
  2491.         IF theTrap >= NumToolboxTraps THEN
  2492.             theTrap := UnimNb;
  2493.         END;
  2494.     { here theTrap has been converted a trap NUMBER }
  2495.  
  2496.     { on 64K ROM machines, we need to check that the trap number matches the trap type }
  2497.     IF (NOT qNeedsROM128k) & (NOT gConfiguration.hasROM128k) THEN
  2498.         IF ((theTrap<$050) | (theTrap=$054) | (theTrap=$057)) <> (theTrapType = OsTrap) THEN
  2499.             theTrap := UnimNb;
  2500.  
  2501.     { finaly check if the trap has the same address has the Unimplemented trap. }
  2502.     { note that we pass GetTrapAddress a trap NUMBER, as documented, not a trap WORD }
  2503.     TrapExists := NGetTrapAddress(UnimNb, ToolTrap) <>
  2504.                   NGetTrapAddress(theTrap, theTrapType);
  2505.     END;
  2506. {$Pop}
  2507.  
  2508. {--------------------------------------------------------------------------------------------------}
  2509. {$S MAUtilitiesRes}
  2510.  
  2511. FUNCTION UprChar(ch: CHAR): CHAR;
  2512.  
  2513.     BEGIN
  2514.     IF (ch IN ['a'..'z']) THEN
  2515.         UprChar := CHR(Ord(ch) - 32)
  2516.     ELSE
  2517.         UprChar := ch;
  2518.     END;
  2519.  
  2520. {--------------------------------------------------------------------------------------------------}
  2521. {$S MAUtilitiesRes}
  2522.  
  2523. PROCEDURE UprStr255(VAR s: Str255);
  2524.  
  2525.     VAR
  2526.         i:                    INTEGER;
  2527.  
  2528.     BEGIN
  2529.     FOR i := 1 TO LENGTH(s) DO
  2530.         IF (s[i] IN ['a'..'z']) THEN
  2531.             s[i] := CHR(Ord(s[i]) - 32)
  2532.     END;
  2533.  
  2534. {--------------------------------------------------------------------------------------------------}
  2535. {$S MAUtilitiesRes}
  2536.  
  2537. PROCEDURE UprMAName(VAR s: MAName);
  2538.  
  2539.     VAR
  2540.         i:                    INTEGER;
  2541.  
  2542.     BEGIN
  2543.     FOR i := 1 TO LENGTH(s) DO
  2544.         IF (s[i] IN ['a'..'z']) THEN
  2545.             s[i] := CHR(Ord(s[i]) - 32)
  2546.     END;
  2547.  
  2548. {--------------------------------------------------------------------------------------------------}
  2549. {$S MAUtilitiesRes}
  2550.  
  2551. PROCEDURE UseROMMap(resLoad: Boolean);
  2552.  
  2553.     BEGIN
  2554.     IF qNeedsROM128K | gConfiguration.hasROM128K THEN
  2555.         BEGIN
  2556.         IF resLoad THEN
  2557.             GetROMMapInsert^ := kLMmapTrue
  2558.         ELSE
  2559.             GetROMMapInsert^ := kLMmapFalse;
  2560.         END
  2561.     ELSE
  2562.         SetResLoad(resLoad);
  2563.     END;
  2564.  
  2565. {--------------------------------------------------------------------------------------------------}
  2566. {$S MADebug}
  2567.  
  2568. FUNCTION VerboseIsHandle(h: UNIV Handle): Boolean;
  2569.  
  2570.     CONST
  2571.         kUnInitStorage1     = $72677267;                { Pascal provided uninited storage }
  2572.         kUnInitStorage2     = $67726772;                { odd byte boundary of above }
  2573.         kDebugHandleInit    = $F3F3F3F3;                { Handles are inited to this in MacApp® }
  2574.         kDebugPtrInit        = $F5F5F5F5;                { Pointers are inited to this in MacApp® }
  2575.         kDebugObjInit        = $F1F1F1F1;                { Objects are inited to this in MacApp® }
  2576.  
  2577.     VAR
  2578.         masterPointer:        Ptr;
  2579.  
  2580.     BEGIN
  2581.     VerboseIsHandle := FALSE;
  2582.  
  2583.     IF Odd(Ord(h)) THEN
  2584.         BEGIN
  2585.         IF Ord(h) = kUnInitStorage1 THEN
  2586.             WriteLn('  That handle appears to be from uninitialized storage.')
  2587.         ELSE IF (Ord(h) = kDebugHandleInit) THEN
  2588.             WriteLn('  That handle appears to be from a handle initialized by debugging.')
  2589.         ELSE IF (Ord(h) = kDebugPtrInit) THEN
  2590.             WriteLn('  That handle appears to be from a pointer initialized by debugging.')
  2591.         ELSE IF (Ord(h) = kDebugObjInit) THEN
  2592.             WriteLn('  That handle appears to be an uninitialized instance variable.')
  2593.         ELSE
  2594.             WriteLn('  That handle is odd.');
  2595.         END
  2596.     ELSE IF Ord(h) = kUnInitStorage2 THEN
  2597.         WriteLn('  That handle appears to be from uninitialized storage.')
  2598.     ELSE IF h = NIL THEN
  2599.         WriteLn('  That handle is NIL.')
  2600.     ELSE
  2601.         BEGIN
  2602.         masterPointer := Ptr(StripLong(h^));
  2603.         IF Odd(Ord(masterPointer)) THEN
  2604.             WriteLn('  The master pointer is odd.')
  2605.         ELSE IF IsFreeHandle(h) THEN
  2606.             WriteLn('  The handle has been freed.')
  2607.         ELSE IF ((masterPointer <> NIL) & NOT TestRecoverHandle(masterPointer, h)) THEN
  2608.             WriteLn('  The alleged heap header is invalid.')
  2609.         ELSE
  2610.             VerboseIsHandle := TRUE;
  2611.         END;
  2612.     END;
  2613.  
  2614. {--------------------------------------------------------------------------------------------------}
  2615. {$S MAUtilitiesRes}
  2616.  
  2617. PROCEDURE WithApplicationResFileDo(PROCEDURE DoWithResFile);
  2618. {??? Needs a failure handler ???}
  2619.  
  2620.     VAR
  2621.         oldResFile:         INTEGER;
  2622.  
  2623.     BEGIN
  2624.     oldResFile := CurResFile;
  2625.     UseResFile(gApplicationRefNum);
  2626.     DoWithResFile;
  2627.     UseResFile(oldResFile);
  2628.     END;
  2629.  
  2630. {--------------------------------------------------------------------------------------------------}
  2631. {$S WWSeg}
  2632.  
  2633. PROCEDURE WriteHandleContents(theHandle: UNIV Handle);
  2634.  
  2635.     VAR
  2636.         Max, index:         Size;
  2637.         wasLocked:            Boolean;
  2638.  
  2639.     BEGIN
  2640.     Max := GetHandleSize(theHandle) - 1;
  2641.     IF Max > 0 THEN
  2642.         BEGIN
  2643.         wasLocked := IsHandleLocked(theHandle);
  2644.         IF NOT wasLocked THEN
  2645.             HLock(theHandle);
  2646.         FOR index := 0 TO Max DO
  2647.             Write(CHR(Ptr(Ord(theHandle^) + index)^));
  2648.         IF NOT wasLocked THEN
  2649.             HUnLock(theHandle);
  2650.         END
  2651.     ELSE
  2652.         Write('**Empty**');
  2653.     END;
  2654.  
  2655. {--------------------------------------------------------------------------------------------------}
  2656. {$S WWSeg}
  2657.  
  2658. PROCEDURE WrLblHandleContents(aLabel: Str255;
  2659.                               theHandle: UNIV Handle);
  2660.  
  2661.     BEGIN
  2662.     Write(aLabel, ' = '); WriteHandleContents(theHandle);
  2663.     END;
  2664.  
  2665. {--------------------------------------------------------------------------------------------------}
  2666. {$S WWSeg}
  2667.  
  2668. PROCEDURE WritePt(pt: Point);
  2669.  
  2670.     VAR
  2671.         theString:            Str255;
  2672.  
  2673.     BEGIN
  2674.     FieldToString(@pt, bPoint, theString);
  2675.     Write(theString);
  2676.     END;
  2677.  
  2678. {--------------------------------------------------------------------------------------------------}
  2679. {$S WWSeg}
  2680.  
  2681. PROCEDURE WrLblPt(aLabel: Str255;
  2682.                   pt: Point);
  2683.  
  2684.     BEGIN
  2685.     Write(aLabel, ' = '); WritePt(pt);
  2686.     END;
  2687.  
  2688. {--------------------------------------------------------------------------------------------------}
  2689. {$S WWSeg}
  2690.  
  2691. PROCEDURE WritePtr(val: UNIV longint);
  2692.  
  2693.     VAR
  2694.         theString:            Str255;
  2695.  
  2696.     BEGIN
  2697.     FieldToString(@val, bPointer, theString);
  2698.     Write(theString);
  2699.     END;
  2700.  
  2701. {--------------------------------------------------------------------------------------------------}
  2702. {$S WWSeg}
  2703.  
  2704. PROCEDURE WrLblPtr(aLabel: Str255;
  2705.                    val: UNIV longint);
  2706.  
  2707.     BEGIN
  2708.     Write(aLabel, ' = '); WritePtr(val);
  2709.     END;
  2710.  
  2711. {--------------------------------------------------------------------------------------------------}
  2712. {$S WWSeg}
  2713.  
  2714. PROCEDURE WriteRect(r: Rect);
  2715.  
  2716.     VAR
  2717.         theString:            Str255;
  2718.  
  2719.     BEGIN
  2720.     FieldToString(@r, bRect, theString);
  2721.     Write(theString);
  2722.     END;
  2723.  
  2724. {--------------------------------------------------------------------------------------------------}
  2725. {$S WWSeg}
  2726.  
  2727. PROCEDURE WrLblRect(aLabel: Str255;
  2728.                     r: Rect);
  2729.  
  2730.     BEGIN
  2731.     Write(aLabel, ' = '); WriteRect(r);
  2732.     END;
  2733.  
  2734. {--------------------------------------------------------------------------------------------------}
  2735. {$S WWSeg}
  2736.  
  2737. PROCEDURE WriteBoolean(b: Boolean);
  2738.  
  2739.     VAR
  2740.         theString:            Str255;
  2741.  
  2742.     BEGIN
  2743.     FieldToString(@b, bBoolean, theString);
  2744.     Write(theString);
  2745.     END;
  2746.  
  2747. {--------------------------------------------------------------------------------------------------}
  2748. {$S WWSeg}
  2749.  
  2750. PROCEDURE WrLblBoolean(aLabel: Str255;
  2751.                        b: Boolean);
  2752.  
  2753.     BEGIN
  2754.     Write(aLabel, ' = ');
  2755.     WriteBoolean(b);
  2756.     END;
  2757.  
  2758. {--------------------------------------------------------------------------------------------------}
  2759. {$S WWSeg}
  2760.  
  2761. PROCEDURE WriteVPt(pt: VPoint);
  2762.  
  2763.     VAR
  2764.         theString:            Str255;
  2765.  
  2766.     BEGIN
  2767.     FieldToString(@pt, bVPoint, theString);
  2768.     Write(theString);
  2769.     END;
  2770.  
  2771. {--------------------------------------------------------------------------------------------------}
  2772. {$S WWSeg}
  2773.  
  2774. PROCEDURE WrLblVPt(aLabel: Str255;
  2775.                    pt: VPoint);
  2776.  
  2777.     BEGIN
  2778.     Write(aLabel, ' = '); WriteVPt(pt);
  2779.     END;
  2780.  
  2781. {--------------------------------------------------------------------------------------------------}
  2782. {$S WWSeg}
  2783.  
  2784. PROCEDURE WriteVRect(r: VRect);
  2785.  
  2786.     VAR
  2787.         theString:            Str255;
  2788.  
  2789.     BEGIN
  2790.     FieldToString(@r, bVRect, theString);
  2791.     Write(theString);
  2792.     END;
  2793.  
  2794. {--------------------------------------------------------------------------------------------------}
  2795. {$S WWSeg}
  2796.  
  2797. PROCEDURE WrLblVRect(aLabel: Str255;
  2798.                      r: VRect);
  2799.  
  2800.     BEGIN
  2801.     Write(aLabel, ' = '); WriteVRect(r);
  2802.     END;
  2803.  
  2804. {--------------------------------------------------------------------------------------------------}
  2805. {$S WWSeg}
  2806.  
  2807. PROCEDURE WriteSig(theID: IDType);
  2808.  
  2809.     VAR
  2810.         theString:            Str255;
  2811.  
  2812.     BEGIN
  2813.     FieldToString(@theID, bIdType, theString);
  2814.     Write(theString);
  2815.     END;
  2816.  
  2817. {--------------------------------------------------------------------------------------------------}
  2818. {$S WWSeg}
  2819.  
  2820. PROCEDURE WrLblSig(theLabel: Str255;
  2821.                    theID: IDType);
  2822.  
  2823.     BEGIN
  2824.     Write(theLabel, ' = '); WriteSig(theID);
  2825.     END;
  2826.  
  2827. {--------------------------------------------------------------------------------------------------}
  2828. {$S WWSeg}
  2829.  
  2830. PROCEDURE WriteHexInt(theInt: INTEGER);
  2831.  
  2832.     VAR
  2833.         theString:            Str255;
  2834.  
  2835.     BEGIN
  2836.     FieldToString(@theInt, bHexInteger, theString);
  2837.     Write(theString);
  2838.     END;
  2839.  
  2840. {--------------------------------------------------------------------------------------------------}
  2841. {$S WWSeg}
  2842.  
  2843. PROCEDURE WrLblHexInt(theLabel: Str255;
  2844.                       theInt: INTEGER);
  2845.  
  2846.     BEGIN
  2847.     Write(theLabel, ' = '); WriteHexInt(theInt);
  2848.     END;
  2849.  
  2850. {--------------------------------------------------------------------------------------------------}
  2851. {$S WWSeg}
  2852.  
  2853. PROCEDURE WriteHexLongint(theLongint: longint);
  2854.  
  2855.     VAR
  2856.         theString:            Str255;
  2857.  
  2858.     BEGIN
  2859.     FieldToString(@theLongint, bHexLongInt, theString);
  2860.     Write(theString);
  2861.     END;
  2862.  
  2863. {--------------------------------------------------------------------------------------------------}
  2864. {$S WWSeg}
  2865.  
  2866. PROCEDURE WrLblHexLongint(theLabel: Str255;
  2867.                           theLongint: longint);
  2868.  
  2869.     BEGIN
  2870.     Write(theLabel, ' = '); WriteHexLongint(theLongint);
  2871.     END;
  2872.